]> Sergey Matveev's repositories - mmc.git/blob - cmd/mmc/post.go
internal package
[mmc.git] / cmd / mmc / post.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         "net/http"
21         "os"
22         "os/exec"
23         "path"
24         "strings"
25         "time"
26
27         "github.com/davecgh/go-spew/spew"
28         "github.com/mattermost/mattermost-server/v6/model"
29         "go.cypherpunks.ru/recfile"
30         "go.stargrave.org/mmc/internal"
31 )
32
33 const CmdFile = "/FILE "
34
35 func writePosts(where string, users map[string]*model.User, posts []mmc.Post) error {
36         if len(posts) == 0 {
37                 return nil
38         }
39         unlock, err := mmc.Lock(path.Join(where, mmc.OutRecLock))
40         if err != nil {
41                 return err
42         }
43         defer unlock()
44         fd, err := os.OpenFile(
45                 path.Join(where, mmc.OutRec),
46                 os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666,
47         )
48         if err != nil {
49                 return err
50         }
51         defer fd.Close()
52         w := recfile.NewWriter(fd)
53         var lastPostId string
54         for _, post := range posts {
55                 if err = mmc.PostToRec(w, users, post); err != nil {
56                         return err
57                 }
58                 lastPostId = post.P.Id
59         }
60         tmp, err := os.CreateTemp(where, "last")
61         if err != nil {
62                 return err
63         }
64         err = os.Chmod(tmp.Name(), os.FileMode(0666&^UmaskCur))
65         if err != nil {
66                 tmp.Close()
67                 os.Remove(tmp.Name())
68                 return err
69         }
70         if _, err = tmp.WriteString(lastPostId + "\n"); err != nil {
71                 os.Remove(tmp.Name())
72                 tmp.Close()
73                 return err
74         }
75         if err = tmp.Close(); err != nil {
76                 os.Remove(tmp.Name())
77                 return err
78         }
79         if err = os.Rename(tmp.Name(), path.Join(where, mmc.Last)); err != nil {
80                 os.Remove(tmp.Name())
81                 return err
82         }
83         unlock()
84         if *Newwin != "" {
85                 exec.Command(*Newwin, where).Run()
86         }
87         return nil
88 }
89
90 func updatePosts(c *model.Client4, users map[string]*model.User, where, chId string) error {
91         data, err := os.ReadFile(path.Join(where, mmc.Last))
92         if err != nil {
93                 if os.IsNotExist(err) {
94                         return nil
95                 }
96                 return err
97         }
98         lastPostId := strings.TrimRight(string(data), "\n")
99         if lastPostId == "" {
100                 return nil
101         }
102         for n := 0; ; n++ {
103                 time.Sleep(mmc.SleepTime)
104                 list, resp, err := c.GetPostsAfter(
105                         chId, lastPostId, n, mmc.PerPage, "", false,
106                 )
107                 if DebugFd != nil {
108                         spew.Fdump(DebugFd, list, resp)
109                 }
110                 if resp.StatusCode == http.StatusNotFound {
111                         break
112                 }
113                 if err != nil {
114                         return err
115                 }
116                 var posts []mmc.Post
117                 for _, post := range list.ToSlice() {
118                         posts = append(posts, mmc.Post{P: post, E: model.WebsocketEventPosted})
119                 }
120                 if err = writePosts(where, users, posts); err != nil {
121                         return err
122                 }
123                 if len(posts) < mmc.PerPage {
124                         break
125                 }
126         }
127         return nil
128 }
129
130 func makePost(c *model.Client4, chId, text string) (*model.Post, error) {
131         text = strings.TrimRight(text, " \n")
132         if text == "" {
133                 return nil, nil
134         }
135         post := &model.Post{ChannelId: chId}
136         var lines []string
137         for _, line := range strings.Split(text, "\n") {
138                 if !strings.HasPrefix(line, CmdFile) {
139                         lines = append(lines, line)
140                         continue
141                 }
142                 fn := strings.TrimPrefix(line, CmdFile)
143                 data, err := os.ReadFile(fn)
144                 if err != nil {
145                         return nil, err
146                 }
147                 resp, _, err := c.UploadFile(data, chId, path.Base(fn))
148                 if err != nil {
149                         return nil, err
150                 }
151                 post.FileIds = append(post.FileIds, resp.FileInfos[0].Id)
152         }
153         post.Message = strings.Join(lines, "\n")
154         post, _, err := c.CreatePost(post)
155         return post, err
156 }