]> Sergey Matveev's repositories - mmc.git/blob - cmd/rd/main.go
internal package
[mmc.git] / cmd / rd / main.go
1 // mmc -- Mattermost client
2 // Copyright (C) 2023-2024 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/internal"
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         for _, m := range ms {
107                 rememberPost(m)
108         }
109         if len(ms) > *lastNum {
110                 ms = ms[len(ms)-*lastNum:]
111         }
112         for _, m := range ms {
113                 printPost(m)
114         }
115
116         watcher, err := fsnotify.NewWatcher()
117         if err != nil {
118                 log.Fatalln(err)
119         }
120         if err = watcher.Add(pth); err != nil {
121                 log.Fatalln(err)
122         }
123         for {
124                 select {
125                 case err = <-watcher.Errors:
126                         log.Fatalln(err)
127                 case event := <-watcher.Events:
128                         if !event.Has(fsnotify.Write) {
129                                 continue
130                         }
131                 }
132                 unlock, err = mmc.Lock(lockPth)
133                 if err != nil {
134                         log.Fatalln(err)
135                 }
136                 r = recfile.NewReader(fd)
137                 for {
138                         m, err := r.NextMapWithSlice()
139                         if err != nil {
140                                 if err == io.EOF {
141                                         break
142                                 }
143                                 log.Fatalln(err)
144                         }
145                         rememberPost(m)
146                         printPost(m)
147                 }
148                 unlock()
149         }
150 }