]> Sergey Matveev's repositories - mmc.git/blob - cmd/rd/main.go
More compact [FILE]
[mmc.git] / cmd / rd / main.go
1 // mmc -- Mattermost client
2 // Copyright (C) 2023 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU Affero General Public License for more details.
13 //
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 package main
18
19 import (
20         "flag"
21         "fmt"
22         "io"
23         "log"
24         "os"
25         "path"
26         "strings"
27         "unicode/utf8"
28
29         "github.com/fsnotify/fsnotify"
30         "github.com/mattermost/mattermost-server/v6/model"
31         "go.cypherpunks.ru/recfile"
32         "go.stargrave.org/mmc"
33 )
34
35 var Threads = make(map[string]string)
36
37 func rememberPost(post map[string][]string) {
38         text := strings.Split(post["Text"][0], "\n")[0]
39         i := 60
40         if len(text) > i {
41                 for i > 0 && !utf8.Valid([]byte{text[i]}) {
42                         i--
43                 }
44                 text = text[:i] + "..."
45         }
46         Threads[post["Id"][0]] = text
47 }
48
49 func printPost(m map[string][]string) {
50         var tag string
51         if len(m["Event"]) > 0 {
52                 switch m["Event"][0] {
53                 case model.WebsocketEventPostEdited:
54                         tag += "[EDIT] "
55                 case model.WebsocketEventPostDeleted:
56                         tag += "[DEL] "
57                 }
58         }
59         var re string
60         if len(m["RootId"]) > 0 {
61                 thread := Threads[m["RootId"][0]]
62                 if thread == "" {
63                         thread = m["RootId"][0]
64                 }
65                 re = " [RE]: " + thread
66         }
67         fmt.Printf(
68                 "\a%s <%s> %s%s%s\n",
69                 m["Created"][0], m["Sender"][0],
70                 tag, m["Text"][0], re,
71         )
72         tag += "[FILE] "
73         for i, fileId := range m["File"] {
74                 fmt.Printf("\a%s%s (%s)\n", tag, fileId, m["FileName"][i])
75         }
76 }
77
78 func main() {
79         lastNum := flag.Int("last", 10, "Only that number of messages")
80         flag.Parse()
81         where := flag.Arg(0)
82         lockPth := path.Join(where, mmc.OutRecLock)
83         unlock, err := mmc.Lock(lockPth)
84         if err != nil {
85                 log.Fatalln(err)
86         }
87         pth := path.Join(where, mmc.OutRec)
88         fd, err := os.Open(pth)
89         if err != nil {
90                 log.Fatalln(err)
91         }
92
93         r := recfile.NewReader(fd)
94         ms := make([]map[string][]string, 0)
95         for {
96                 m, err := r.NextMapWithSlice()
97                 if err != nil {
98                         if err == io.EOF {
99                                 break
100                         }
101                         log.Fatalln(err)
102                 }
103                 ms = append(ms, m)
104         }
105         unlock()
106         if len(ms) > *lastNum {
107                 ms = ms[len(ms)-*lastNum:]
108         }
109         for _, m := range ms {
110                 rememberPost(m)
111                 printPost(m)
112         }
113
114         watcher, err := fsnotify.NewWatcher()
115         if err != nil {
116                 log.Fatalln(err)
117         }
118         if err = watcher.Add(pth); err != nil {
119                 log.Fatalln(err)
120         }
121         for {
122                 select {
123                 case err = <-watcher.Errors:
124                         log.Fatalln(err)
125                 case event := <-watcher.Events:
126                         if !event.Has(fsnotify.Write) {
127                                 continue
128                         }
129                 }
130                 unlock, err = mmc.Lock(lockPth)
131                 if err != nil {
132                         log.Fatalln(err)
133                 }
134                 r = recfile.NewReader(fd)
135                 for {
136                         m, err := r.NextMapWithSlice()
137                         if err != nil {
138                                 if err == io.EOF {
139                                         break
140                                 }
141                                 log.Fatalln(err)
142                         }
143                         rememberPost(m)
144                         printPost(m)
145                 }
146                 unlock()
147         }
148 }