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