// package main is the main package for the LapisDeploy program package main import ( "context" "log" "strings" "time" "maunium.net/go/mautrix" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" "maunium.net/go/mautrix/id" ) // MatrixMessage stores information about a matrix message. type MatrixMessage struct { text string level int } // MatrixCommandCallback is a function that gets called when the bot is issued a command over Matrix type MatrixCommandCallback func(ctx context.Context, evt *event.Event, args []string) string // MatrixCommand stores information about a matrix chat command type MatrixCommand struct { cmd string callback MatrixCommandCallback description string } // Output queue var MatrixOut []MatrixMessage // sendMessage sends a message through Matrix func sendMessage(msg MatrixMessage) { log.Print(msg.text) MatrixOut = append(MatrixOut, msg) } // initMatrix starts a Matrix bot. func initMatrix() { client, err := mautrix.NewClient(configuration.matrix.homeserver, "", "") if err != nil { log.Fatal(err) } resp, err := client.Login(context.Background(), &mautrix.ReqLogin{ Type: "m.login.password", Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: configuration.matrix.username}, Password: configuration.matrix.password, StoreCredentials: true, }) if err != nil { log.Panic(err) } log.Printf("[Matrix] Logged in as %s", resp.UserID) syncer := client.Syncer.(*mautrix.DefaultSyncer) client.Syncer.(mautrix.ExtensibleSyncer).OnSync(client.DontProcessOldEvents) syncer.OnEventType(event.StateMember, func(ctx context.Context, evt *event.Event) { if evt.GetStateKey() == client.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite { if evt.RoomID.String() == configuration.matrix.room_id { _, err := client.JoinRoomByID(ctx, evt.RoomID) if err == nil { log.Printf("[Matrix] Joined room '%s' (Invited by '%s')", evt.RoomID.String(), evt.Sender.String()) } else { log.Printf("[Matrix] Failed to join room '%s' after invite (Invited by '%s')", evt.RoomID.String(), evt.Sender.String()) } } else { log.Printf("[Matrix] Rejected invite to room '%s' (Invited by '%s')", evt.RoomID.String(), evt.Sender.String()) } } }) syncer.OnEventType(event.EventMessage, func(ctx context.Context, evt *event.Event) { if evt.RoomID.String() == configuration.matrix.room_id { prefixes := []string{"!", configuration.matrix.username + " "} for _, prefix := range prefixes { if strings.HasPrefix(evt.Content.AsMessage().Body, prefix) { msg := evt.Content.AsMessage().Body cmd := strings.TrimPrefix(strings.Split(msg, " ")[0], prefix) found_command := false for _, command := range Chatcommands { if command.cmd == cmd { found_command = true out := command.callback(ctx, evt, strings.Split(strings.TrimPrefix(msg, prefix+cmd), " ")) content := format.RenderMarkdown(out, true, false) content.SetReply(evt) client.SendMessageEvent(ctx, evt.RoomID, event.EventMessage, content) } } if !found_command { client.SendText(ctx, evt.RoomID, Circles["red"]+"Command not found") } break } } } }) registerChatCommands() // Register all the chat commands defined inside matrix_commands.go go func() { for range time.Tick(time.Second * 2) { log.Printf("Scanning for queued messages...") if len(MatrixOut) > 0 { text := "" for i, msg := range MatrixOut { text += msg.text if i != len(MatrixOut) { text += "\n" } } MatrixOut = []MatrixMessage{} _, err := client.SendMessageEvent(context.Background(), id.RoomID(configuration.matrix.room_id), event.EventMessage, format.RenderMarkdown(text, true, false), ) if err != nil { log.Printf("[Matrix] ERROR : %s", err) continue } log.Print("[Matrix] Sent queued message(s) successfully.") } } }() go func() { log.Print("[Matrix] Running!") err = client.Sync() if err != nil { log.Fatal(err) } }() }