// package main is the main package for the LapisDeploy program package main import ( "context" "fmt" "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 } // 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 // ChatCommands var Chatcommands []MatrixCommand // sendMessage sends a message through Matrix func sendMessage(msg MatrixMessage) { MatrixOut = append(MatrixOut, msg) } // registerChatCommand func registerChatCommand(cmd string, description string, callback MatrixCommandCallback) { Chatcommands = append(Chatcommands, MatrixCommand{ cmd: cmd, callback: callback, description: description, }) } // 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, "šŸ”“ Command not found") } break } } } }) registerChatCommand("help", "Show the help dialouge", func(ctx context.Context, evt *event.Event, args []string) string { text := "" for _, cmd := range Chatcommands { text = fmt.Sprintf("%s- %s\n %s\n", text, cmd.cmd, cmd.description) } return text }) registerChatCommand("sites", "Display all available sites", func(ctx context.Context, evt *event.Event, args []string) string { text := "āšŖļø Getting sites...\n" sites, err := getAllSites() if err.code != 0 { return text + "šŸ”“ Failed to get sites!" } for _, site := range sites { text = fmt.Sprintf("%s- %s\n", text, site.name) } if len(sites) == 0 { text = text + "*No sites found*" } return text }) registerChatCommand("restart", "Restart a site.", func(ctx context.Context, evt *event.Event, args []string) string { if len(args) < 2 { return "šŸ”“ Invalid site." } site, found, err := getSite(args[1]) if err != nil { return "šŸ”“ Failed to get site " + args[2] + "!" } else if !found { return "šŸ”“ Invalid site." } text := "āšŖļø Restarting server..." derr := site.Restart() if derr.code != 0 { derr.SendOverMatrix() return text + "\nšŸ”“ Failed to restart site!" } return text + "\nšŸŸ¢ Successfully restarted site." }) go func() { for range time.Tick(time.Second * 6) { log.Printf("Scanning for queued messages...") for _, msg := range MatrixOut { _, err := client.SendMessageEvent(context.Background(), id.RoomID(configuration.matrix.room_id), event.EventMessage, format.RenderMarkdown(msg.text, true, false)) if err != nil { log.Printf("[Matrix] ERROR : %s", err) continue } log.Print("[Matrix] Sent queued message successfully.") } MatrixOut = []MatrixMessage{} } }() go func() { log.Print("[Matrix] Running!") err = client.Sync() if err != nil { log.Fatal(err) } }() }