// mmc -- Mattermost client // Copyright (C) 2023 Sergey Matveev // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package main import ( "flag" "fmt" "io" "log" "os" "path" "strings" "unicode/utf8" "github.com/fsnotify/fsnotify" "github.com/mattermost/mattermost-server/v6/model" "go.cypherpunks.ru/recfile" "go.stargrave.org/mmc" ) var Threads = make(map[string]string) func rememberPost(post map[string][]string) { text := strings.Split(post["Text"][0], "\n")[0] i := 60 if len(text) > i { for i > 0 && !utf8.Valid([]byte{text[i]}) { i-- } text = text[:i] + "..." } Threads[post["Id"][0]] = text } func printPost(m map[string][]string) { var tag string if len(m["Event"]) > 0 { switch m["Event"][0] { case model.WebsocketEventPostEdited: tag += "[EDIT] " case model.WebsocketEventPostDeleted: tag += "[DEL] " } } var re string if len(m["RootId"]) > 0 { thread := Threads[m["RootId"][0]] if thread == "" { thread = m["RootId"][0] } re = " [RE]: " + thread } fmt.Printf( "\a%s <%s> %s%s%s\n", m["Created"][0], m["Sender"][0], tag, m["Text"][0], re, ) tag += "[FILE] " for i, fileId := range m["File"] { fmt.Printf("\a%s%s (%s)\n", tag, fileId, m["FileName"][i]) } } func main() { lastNum := flag.Int("last", 10, "Only that number of messages") flag.Parse() where := flag.Arg(0) lockPth := path.Join(where, mmc.OutRecLock) unlock, err := mmc.Lock(lockPth) if err != nil { log.Fatalln(err) } pth := path.Join(where, mmc.OutRec) fd, err := os.Open(pth) if err != nil { log.Fatalln(err) } r := recfile.NewReader(fd) ms := make([]map[string][]string, 0) for { m, err := r.NextMapWithSlice() if err != nil { if err == io.EOF { break } log.Fatalln(err) } ms = append(ms, m) } unlock() for _, m := range ms { rememberPost(m) } if len(ms) > *lastNum { ms = ms[len(ms)-*lastNum:] } for _, m := range ms { printPost(m) } watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatalln(err) } if err = watcher.Add(pth); err != nil { log.Fatalln(err) } for { select { case err = <-watcher.Errors: log.Fatalln(err) case event := <-watcher.Events: if !event.Has(fsnotify.Write) { continue } } unlock, err = mmc.Lock(lockPth) if err != nil { log.Fatalln(err) } r = recfile.NewReader(fd) for { m, err := r.NextMapWithSlice() if err != nil { if err == io.EOF { break } log.Fatalln(err) } rememberPost(m) printPost(m) } unlock() } }