snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
446
server/cmd/mmctl/commands/post.go
Normal file
446
server/cmd/mmctl/commands/post.go
Normal file
@@ -0,0 +1,446 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
|
||||
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
|
||||
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var PostCmd = &cobra.Command{
|
||||
Use: "post",
|
||||
Short: "Management of posts",
|
||||
}
|
||||
|
||||
var PostCreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create a post",
|
||||
Long: "Create a post in a channel or send a direct message to a user by prefixing the user with '@'.",
|
||||
Example: ` post create myteam:mychannel --message "some text for the post"
|
||||
post create @target-user --message "some text for the direct message"
|
||||
post create myteam:mychannel --message "see attachment" --file ./report.pdf --file ./image.png`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: withClient(postCreateCmdF),
|
||||
}
|
||||
|
||||
var PostListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List posts for a channel",
|
||||
Example: ` post list myteam:mychannel
|
||||
post list myteam:mychannel --number 20`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: withClient(postListCmdF),
|
||||
}
|
||||
|
||||
var PostRevealCmd = &cobra.Command{
|
||||
Use: "reveal [post]",
|
||||
Short: "Reveal a post",
|
||||
Example: ` post reveal udjmt396tjghi8wnsk3a1qs1sw`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: withClient(revealPostCmdF),
|
||||
}
|
||||
|
||||
var PostDeleteCmd = &cobra.Command{
|
||||
Use: "delete [posts]",
|
||||
Short: "Mark posts as deleted or permanently delete posts with the --permanent flag",
|
||||
Long: `This command will mark the post as deleted and remove it from the user's clients, but it does not permanently delete the post from the database. Please use the --permanent flag to permanently delete a post and its attachments from your database.`,
|
||||
Example: ` # Mark Post as deleted
|
||||
$ mmctl post delete udjmt396tjghi8wnsk3a1qs1sw
|
||||
|
||||
# Permanently delete a post and it's file contents from the database and filestore
|
||||
$ mmctl post delete udjmt396tjghi8wnsk3a1qs1sw --permanent
|
||||
|
||||
# Permanently delete multiple posts and their file contents from the database and filestore
|
||||
$ mmctl post delete udjmt396tjghi8wnsk3a1qs1sw 7jgcjt7tyjyyu83qz81wo84w6o --permanent`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: withClient(deletePostsCmdF),
|
||||
}
|
||||
|
||||
const (
|
||||
ISO8601Layout = "2006-01-02T15:04:05-07:00"
|
||||
PostTimeFormat = "2006-01-02 15:04:05-07:00"
|
||||
|
||||
directMessagePrefix = "@"
|
||||
)
|
||||
|
||||
func init() {
|
||||
PostCreateCmd.Flags().StringP("message", "m", "", "Message for the post")
|
||||
PostCreateCmd.Flags().StringP("reply-to", "r", "", "Post id to reply to")
|
||||
PostCreateCmd.Flags().BoolP("burn-on-read", "b", false, "Message will be deleted after a certain time after being read")
|
||||
PostCreateCmd.Flags().StringArrayP("file", "f", nil, "Path to a local file to attach to the post. Can be specified multiple times to attach multiple files")
|
||||
|
||||
PostListCmd.Flags().IntP("number", "n", 20, "Number of messages to list")
|
||||
PostListCmd.Flags().BoolP("show-ids", "i", false, "Show posts ids")
|
||||
PostListCmd.Flags().BoolP("follow", "f", false, "Output appended data as new messages are posted to the channel")
|
||||
PostListCmd.Flags().StringP("since", "s", "", "List messages posted after a certain time (ISO 8601)")
|
||||
|
||||
PostDeleteCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the post and a DB backup has been performed")
|
||||
PostDeleteCmd.Flags().Bool("permanent", false, "Permanently delete the post and its contents from the database")
|
||||
|
||||
PostCmd.AddCommand(
|
||||
PostCreateCmd,
|
||||
PostListCmd,
|
||||
PostDeleteCmd,
|
||||
PostRevealCmd,
|
||||
)
|
||||
|
||||
RootCmd.AddCommand(PostCmd)
|
||||
}
|
||||
|
||||
func postCreateCmdF(c client.Client, cmd *cobra.Command, args []string) error {
|
||||
if viper.GetBool("local") {
|
||||
return errors.New("creating posts is not supported in local mode")
|
||||
}
|
||||
|
||||
message, _ := cmd.Flags().GetString("message")
|
||||
files, _ := cmd.Flags().GetStringArray("file")
|
||||
if message == "" && len(files) == 0 {
|
||||
return errors.New("a post must have a message or at least one file attachment")
|
||||
}
|
||||
|
||||
replyTo, _ := cmd.Flags().GetString("reply-to")
|
||||
if replyTo != "" {
|
||||
replyToPost, _, err := c.GetPost(context.TODO(), replyTo, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if replyToPost.RootId != "" {
|
||||
replyTo = replyToPost.RootId
|
||||
}
|
||||
}
|
||||
|
||||
channelID, err := getPostChannelID(c, args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileIDs, uploadErr, err := uploadPostFiles(c, channelID, files)
|
||||
if err != nil {
|
||||
// A file could not be opened, so nothing was uploaded and no
|
||||
// attachments were orphaned. Abort before creating the post.
|
||||
return err
|
||||
}
|
||||
if message == "" && len(fileIDs) == 0 {
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
return errors.New("a post must have a message or at least one file attachment")
|
||||
}
|
||||
|
||||
post := &model.Post{
|
||||
ChannelId: channelID,
|
||||
Message: message,
|
||||
RootId: replyTo,
|
||||
FileIds: fileIDs,
|
||||
}
|
||||
|
||||
if burnOnRead, _ := cmd.Flags().GetBool("burn-on-read"); burnOnRead {
|
||||
post.Type = model.PostTypeBurnOnRead
|
||||
}
|
||||
|
||||
url := "/posts" + "?set_online=false"
|
||||
data, err := post.ToJSON()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not decode post: %w", err)
|
||||
}
|
||||
|
||||
if _, err := c.DoAPIPost(context.TODO(), url, data); err != nil {
|
||||
return fmt.Errorf("could not create post: %w", err)
|
||||
}
|
||||
|
||||
// Any files that uploaded successfully are attached to the post above, so
|
||||
// a partial batch failure never leaves those uploads orphaned. The error is
|
||||
// returned last so the caller still learns which files could not be sent.
|
||||
return uploadErr
|
||||
}
|
||||
|
||||
// uploadPostFiles streams each local file to the server and returns the IDs of
|
||||
// the files that were uploaded successfully. All files are opened up front so
|
||||
// an unreadable path (returned as the final error) fails before any upload
|
||||
// happens, leaving nothing orphaned. Once uploads start, a failure on one file
|
||||
// does not abort the batch: it is collected in uploadErr while the files that
|
||||
// did upload are still returned so they can be attached to the post.
|
||||
func uploadPostFiles(c client.Client, channelID string, files []string) (fileIDs []string, uploadErr error, err error) {
|
||||
if len(files) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
handles := make([]*os.File, 0, len(files))
|
||||
defer func() {
|
||||
for _, f := range handles {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
for _, file := range files {
|
||||
f, openErr := os.Open(file)
|
||||
if openErr != nil {
|
||||
return nil, nil, fmt.Errorf("could not read file '%s': %w", file, openErr)
|
||||
}
|
||||
handles = append(handles, f)
|
||||
}
|
||||
|
||||
var errs *multierror.Error
|
||||
for i, f := range handles {
|
||||
fileID, uploadFileErr := uploadPostFile(c, channelID, files[i], f)
|
||||
if uploadFileErr != nil {
|
||||
errs = multierror.Append(errs, uploadFileErr)
|
||||
continue
|
||||
}
|
||||
fileIDs = append(fileIDs, fileID)
|
||||
}
|
||||
|
||||
return fileIDs, errs.ErrorOrNil(), nil
|
||||
}
|
||||
|
||||
func uploadPostFile(c client.Client, channelID, path string, f *os.File) (string, error) {
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read file '%s': %w", path, err)
|
||||
}
|
||||
|
||||
us, _, err := c.CreateUpload(context.TODO(), &model.UploadSession{
|
||||
Type: model.UploadTypeAttachment,
|
||||
ChannelId: channelID,
|
||||
Filename: filepath.Base(path),
|
||||
FileSize: info.Size(),
|
||||
UserId: "me",
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not upload file '%s': %w", path, err)
|
||||
}
|
||||
|
||||
fileInfo, _, err := c.UploadData(context.TODO(), us.Id, f)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not upload file '%s': %w", path, err)
|
||||
}
|
||||
if fileInfo == nil {
|
||||
return "", fmt.Errorf("could not upload file '%s': upload did not complete", path)
|
||||
}
|
||||
|
||||
return fileInfo.Id, nil
|
||||
}
|
||||
|
||||
func getPostChannelID(c client.Client, arg string) (string, error) {
|
||||
if username, ok := strings.CutPrefix(arg, directMessagePrefix); ok {
|
||||
channel, err := getDirectChannel(c, username)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return channel.Id, nil
|
||||
}
|
||||
|
||||
channel := getChannelFromChannelArg(c, arg)
|
||||
if channel == nil {
|
||||
return "", errors.New("Unable to find channel '" + arg + "'")
|
||||
}
|
||||
return channel.Id, nil
|
||||
}
|
||||
|
||||
func getDirectChannel(c client.Client, username string) (*model.Channel, error) {
|
||||
user, err := getUserFromArg(c, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
me, _, err := c.GetMe(context.TODO(), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not retrieve the current user: %w", err)
|
||||
}
|
||||
|
||||
channel, _, err := c.CreateDirectChannel(context.TODO(), me.Id, user.Id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create direct channel with '%s': %w", username, err)
|
||||
}
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
func eventDataToPost(eventData map[string]any) (*model.Post, error) {
|
||||
post := &model.Post{}
|
||||
var rawPost string
|
||||
for k, v := range eventData {
|
||||
if k == "post" {
|
||||
rawPost = v.(string)
|
||||
}
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(rawPost), &post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return post, nil
|
||||
}
|
||||
|
||||
func printPost(c client.Client, post *model.Post, usernames map[string]string, showIds, showTimestamp bool) {
|
||||
var username string
|
||||
|
||||
if usernames[post.UserId] != "" {
|
||||
username = usernames[post.UserId]
|
||||
} else {
|
||||
user, _, err := c.GetUser(context.TODO(), post.UserId, "")
|
||||
if err != nil {
|
||||
username = post.UserId
|
||||
} else {
|
||||
usernames[post.UserId] = user.Username
|
||||
username = user.Username
|
||||
}
|
||||
}
|
||||
|
||||
postTime := model.GetTimeForMillis(post.CreateAt)
|
||||
createdAt := postTime.Format(PostTimeFormat)
|
||||
|
||||
var templatedMessage string
|
||||
|
||||
if showTimestamp {
|
||||
templatedMessage = fmt.Sprintf("{{ if eq .Type \"burn_on_read\" }}🔥 {{ end }}\u001b[32m%s\u001b[0m \u001b[34;1m[%s]\u001b[0m {{.Message}}", createdAt, username)
|
||||
} else {
|
||||
if showIds {
|
||||
templatedMessage = fmt.Sprintf("{{ if eq .Type \"burn_on_read\" }}🔥 {{ end }}\u001b[31m%s\u001b[0m \u001b[34;1m[%s]\u001b[0m {{.Message}}", post.Id, username)
|
||||
} else {
|
||||
templatedMessage = fmt.Sprintf("{{ if eq .Type \"burn_on_read\" }}🔥 {{ end }}\u001b[34;1m[%s]\u001b[0m {{.Message}}", username)
|
||||
}
|
||||
}
|
||||
|
||||
if post.Type == model.PostTypeBurnOnRead {
|
||||
expireAt := post.Metadata.ExpireAt
|
||||
if expireAt != 0 {
|
||||
dur := time.Until(time.UnixMilli(expireAt))
|
||||
templatedMessage = fmt.Sprintf("%s (expires in %s)", templatedMessage, dur.String())
|
||||
}
|
||||
}
|
||||
printer.PrintT(templatedMessage, post)
|
||||
}
|
||||
|
||||
func getPostList(client client.Client, channelID, since string, perPage int) (*model.PostList, *model.Response, error) {
|
||||
if since == "" {
|
||||
return client.GetPostsForChannel(context.TODO(), channelID, 0, perPage, "", false, false)
|
||||
}
|
||||
|
||||
sinceTime, err := time.Parse(ISO8601Layout, since)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid since time '%s'", since)
|
||||
}
|
||||
|
||||
sinceTimeMillis := model.GetMillisForTime(sinceTime)
|
||||
return client.GetPostsSince(context.TODO(), channelID, sinceTimeMillis, false)
|
||||
}
|
||||
|
||||
func postListCmdF(c client.Client, cmd *cobra.Command, args []string) error {
|
||||
printer.SetSingle(true)
|
||||
|
||||
channel := getChannelFromChannelArg(c, args[0])
|
||||
if channel == nil {
|
||||
return errors.New("Unable to find channel '" + args[0] + "'")
|
||||
}
|
||||
|
||||
number, _ := cmd.Flags().GetInt("number")
|
||||
showIds, _ := cmd.Flags().GetBool("show-ids")
|
||||
follow, _ := cmd.Flags().GetBool("follow")
|
||||
since, _ := cmd.Flags().GetString("since")
|
||||
|
||||
postList, _, err := getPostList(c, channel.Id, since, number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
posts := postList.ToSlice()
|
||||
showTimestamp := since != ""
|
||||
usernames := map[string]string{}
|
||||
for i := 1; i <= len(posts); i++ {
|
||||
post := posts[len(posts)-i]
|
||||
printPost(c, post, usernames, showIds, showTimestamp)
|
||||
}
|
||||
|
||||
var multiErr *multierror.Error
|
||||
if follow {
|
||||
ws, err := InitWebSocketClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appErr := ws.Connect()
|
||||
if appErr != nil {
|
||||
return errors.New(appErr.Error())
|
||||
}
|
||||
|
||||
ws.Listen()
|
||||
for {
|
||||
event := <-ws.EventChannel
|
||||
if event.EventType() == model.WebsocketEventPosted {
|
||||
post, err := eventDataToPost(event.GetData())
|
||||
if err != nil {
|
||||
printer.PrintError("Error parsing incoming post: " + err.Error())
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
if post.ChannelId == channel.Id {
|
||||
printPost(c, post, usernames, showIds, showTimestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func deletePostsCmdF(c client.Client, cmd *cobra.Command, args []string) error {
|
||||
permanent, err := cmd.Flags().GetBool("permanent")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
confirmFlag, _ := cmd.Flags().GetBool("confirm")
|
||||
if !confirmFlag && permanent {
|
||||
if err = getConfirmation("Are you sure you want to delete the posts specified?", true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var result *multierror.Error
|
||||
var deleteFunc func(ctx context.Context, postID string) (*model.Response, error)
|
||||
|
||||
if permanent {
|
||||
deleteFunc = c.PermanentDeletePost
|
||||
} else {
|
||||
deleteFunc = c.DeletePost
|
||||
}
|
||||
|
||||
for _, postID := range args {
|
||||
isValidId := model.IsValidId(postID)
|
||||
if !isValidId {
|
||||
printer.PrintError(fmt.Sprintf("Invalid postID: %s", postID))
|
||||
result = multierror.Append(result, err)
|
||||
continue
|
||||
}
|
||||
if _, err := deleteFunc(context.TODO(), postID); err != nil {
|
||||
printer.PrintError(fmt.Sprintf("Error deleting post: %s. Error: %s", postID, err.Error()))
|
||||
result = multierror.Append(result, err)
|
||||
continue
|
||||
}
|
||||
printer.Print(fmt.Sprintf("%s successfully deleted", postID))
|
||||
}
|
||||
return result.ErrorOrNil()
|
||||
}
|
||||
|
||||
func revealPostCmdF(c client.Client, cmd *cobra.Command, args []string) error {
|
||||
postID := args[0]
|
||||
post, _, err := c.RevealPost(context.TODO(), postID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printPost(c, post, map[string]string{}, false, false)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user