]> Sergey Matveev's repositories - mmc.git/blob - cmd/rd/main.go
Show thread mention
[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(
75                         "\a%s <%s> %s%s (%s)\n",
76                         m["Created"][0], m["Sender"][0], tag, fileId, m["FileName"][i],
77                 )
78         }
79 }
80
81 func main() {
82         lastNum := flag.Int("last", 10, "Only that number of messages")
83         flag.Parse()
84         where := flag.Arg(0)
85         lockPth := path.Join(where, mmc.OutRecLock)
86         unlock, err := mmc.Lock(lockPth)
87         if err != nil {
88                 log.Fatalln(err)
89         }
90         pth := path.Join(where, mmc.OutRec)
91         fd, err := os.Open(pth)
92         if err != nil {
93                 log.Fatalln(err)
94         }
95
96         r := recfile.NewReader(fd)
97         ms := make([]map[string][]string, 0)
98         for {
99                 m, err := r.NextMapWithSlice()
100                 if err != nil {
101                         if err == io.EOF {
102                                 break
103                         }
104                         log.Fatalln(err)
105                 }
106                 ms = append(ms, m)
107         }
108         unlock()
109         if len(ms) > *lastNum {
110                 ms = ms[len(ms)-*lastNum:]
111         }
112         for _, m := range ms {
113                 rememberPost(m)
114                 printPost(m)
115         }
116
117         watcher, err := fsnotify.NewWatcher()
118         if err != nil {
119                 log.Fatalln(err)
120         }
121         if err = watcher.Add(pth); err != nil {
122                 log.Fatalln(err)
123         }
124         for {
125                 select {
126                 case err = <-watcher.Errors:
127                         log.Fatalln(err)
128                 case event := <-watcher.Events:
129                         if !event.Has(fsnotify.Write) {
130                                 continue
131                         }
132                 }
133                 unlock, err = mmc.Lock(lockPth)
134                 if err != nil {
135                         log.Fatalln(err)
136                 }
137                 r = recfile.NewReader(fd)
138                 for {
139                         m, err := r.NextMapWithSlice()
140                         if err != nil {
141                                 if err == io.EOF {
142                                         break
143                                 }
144                                 log.Fatalln(err)
145                         }
146                         rememberPost(m)
147                         printPost(m)
148                 }
149                 unlock()
150         }
151 }