// mmc -- Mattermost client // Copyright (C) 2023-2024 Sergey Matveev // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package main import ( "net/http" "os" "os/exec" "path" "strings" "time" "github.com/davecgh/go-spew/spew" "github.com/mattermost/mattermost-server/v6/model" "go.cypherpunks.ru/recfile" "go.stargrave.org/mmc/internal" ) const CmdFile = "/FILE " func writePosts(where string, users map[string]*model.User, posts []mmc.Post) error { if len(posts) == 0 { return nil } unlock, err := mmc.Lock(path.Join(where, mmc.OutRecLock)) if err != nil { return err } defer unlock() fd, err := os.OpenFile( path.Join(where, mmc.OutRec), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666, ) if err != nil { return err } defer fd.Close() w := recfile.NewWriter(fd) var lastPostId string for _, post := range posts { if err = mmc.PostToRec(w, users, post); err != nil { return err } lastPostId = post.P.Id } tmp, err := os.CreateTemp(where, "last") if err != nil { return err } err = os.Chmod(tmp.Name(), os.FileMode(0666&^UmaskCur)) if err != nil { tmp.Close() os.Remove(tmp.Name()) return err } if _, err = tmp.WriteString(lastPostId + "\n"); err != nil { os.Remove(tmp.Name()) tmp.Close() return err } if err = tmp.Close(); err != nil { os.Remove(tmp.Name()) return err } if err = os.Rename(tmp.Name(), path.Join(where, mmc.Last)); err != nil { os.Remove(tmp.Name()) return err } unlock() if *Newwin != "" { exec.Command(*Newwin, where).Run() } return nil } func updatePosts(c *model.Client4, users map[string]*model.User, where, chId string) error { data, err := os.ReadFile(path.Join(where, mmc.Last)) if err != nil { if os.IsNotExist(err) { return nil } return err } lastPostId := strings.TrimRight(string(data), "\n") if lastPostId == "" { return nil } for n := 0; ; n++ { time.Sleep(mmc.SleepTime) list, resp, err := c.GetPostsAfter( chId, lastPostId, n, mmc.PerPage, "", false, ) if DebugFd != nil { spew.Fdump(DebugFd, list, resp) } if resp.StatusCode == http.StatusNotFound { break } if err != nil { return err } var posts []mmc.Post for _, post := range list.ToSlice() { posts = append(posts, mmc.Post{P: post, E: model.WebsocketEventPosted}) } if err = writePosts(where, users, posts); err != nil { return err } if len(posts) < mmc.PerPage { break } } return nil } func makePost(c *model.Client4, chId, text string) (*model.Post, error) { text = strings.TrimRight(text, " \n") if text == "" { return nil, nil } post := &model.Post{ChannelId: chId} var lines []string for _, line := range strings.Split(text, "\n") { if !strings.HasPrefix(line, CmdFile) { lines = append(lines, line) continue } fn := strings.TrimPrefix(line, CmdFile) data, err := os.ReadFile(fn) if err != nil { return nil, err } resp, _, err := c.UploadFile(data, chId, path.Base(fn)) if err != nil { return nil, err } post.FileIds = append(post.FileIds, resp.FileInfos[0].Id) } post.Message = strings.Join(lines, "\n") post, _, err := c.CreatePost(post) return post, err }