github.com/decred/politeia@v1.4.0/politeiawww/cmd/pictl/cmdcommentnew.go (about)

     1  // Copyright (c) 2020-2021 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"strconv"
    11  
    12  	cmv1 "github.com/decred/politeia/politeiawww/api/comments/v1"
    13  	piv1 "github.com/decred/politeia/politeiawww/api/pi/v1"
    14  	pclient "github.com/decred/politeia/politeiawww/client"
    15  	"github.com/decred/politeia/politeiawww/cmd/shared"
    16  )
    17  
    18  // cmdCommentNew submits a new comment.
    19  type cmdCommentNew struct {
    20  	Args struct {
    21  		Token    string `positional-arg-name:"token" required:"true"`
    22  		Comment  string `positional-arg-name:"comment" required:"true"`
    23  		ParentID uint32 `positional-arg-name:"parentid"`
    24  	} `positional-args:"true"`
    25  
    26  	// Unvetted is used to comment on an unvetted record. If this flag
    27  	// is not used the command assumes the record is vetted.
    28  	Unvetted bool `long:"unvetted" optional:"true"`
    29  
    30  	// UpdateTitle is used to post a new author update.
    31  	UpdateTitle string `long:"updatetitle" optional:"true"`
    32  }
    33  
    34  // Execute executes the cmdCommentNew command.
    35  //
    36  // This function satisfies the go-flags Commander interface.
    37  func (c *cmdCommentNew) Execute(args []string) error {
    38  	// Unpack args
    39  	var (
    40  		token    = c.Args.Token
    41  		comment  = c.Args.Comment
    42  		parentID = c.Args.ParentID
    43  	)
    44  
    45  	// Check for user identity. A user identity is required to sign
    46  	// the comment.
    47  	if cfg.Identity == nil {
    48  		return shared.ErrUserIdentityNotFound
    49  	}
    50  
    51  	// Setup client
    52  	opts := pclient.Opts{
    53  		HTTPSCert:  cfg.HTTPSCert,
    54  		Cookies:    cfg.Cookies,
    55  		HeaderCSRF: cfg.CSRF,
    56  		Verbose:    cfg.Verbose,
    57  		RawJSON:    cfg.RawJSON,
    58  	}
    59  	pc, err := pclient.New(cfg.Host, opts)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	// Setup state
    65  	var state cmv1.RecordStateT
    66  	switch {
    67  	case c.Unvetted:
    68  		state = cmv1.RecordStateUnvetted
    69  	default:
    70  		state = cmv1.RecordStateVetted
    71  	}
    72  
    73  	// Prepare extra data if it's a new author update
    74  	var (
    75  		extraData,
    76  		extraDataHint string
    77  	)
    78  	if c.UpdateTitle != "" {
    79  		extraDataHint = piv1.ProposalUpdateHint
    80  		pum := piv1.ProposalUpdateMetadata{
    81  			Title: c.UpdateTitle,
    82  		}
    83  		b, err := json.Marshal(pum)
    84  		if err != nil {
    85  			return err
    86  		}
    87  		extraData = string(b)
    88  	}
    89  
    90  	// Setup request
    91  	msg := strconv.FormatUint(uint64(state), 10) + token +
    92  		strconv.FormatUint(uint64(parentID), 10) + comment +
    93  		extraData + extraDataHint
    94  	sig := cfg.Identity.SignMessage([]byte(msg))
    95  	n := cmv1.New{
    96  		State:         state,
    97  		Token:         token,
    98  		ParentID:      parentID,
    99  		Comment:       comment,
   100  		Signature:     hex.EncodeToString(sig[:]),
   101  		PublicKey:     cfg.Identity.Public.String(),
   102  		ExtraDataHint: extraDataHint,
   103  		ExtraData:     extraData,
   104  	}
   105  
   106  	// Send request
   107  	nr, err := pc.CommentNew(n)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	// Verify receipt
   113  	vr, err := client.Version()
   114  	if err != nil {
   115  		return err
   116  	}
   117  	err = pclient.CommentVerify(nr.Comment, vr.PubKey)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	// Print receipt
   123  	printComment(nr.Comment)
   124  
   125  	return nil
   126  }
   127  
   128  // commentNewHelpMsg is printed to stdout by the help command.
   129  const commentNewHelpMsg = `commentnew "token" "comment" parentid
   130  
   131  Comment on a record. Requires the user to be logged in.
   132  
   133  This command assumes the record is a vetted record.
   134  
   135  If the record is unvetted, the --unvetted flag must be used. Commenting on
   136  unvetted records requires admin priviledges.
   137  
   138  Proposal's author may post author update using the --updatetitle flag. Author 
   139  updates are allowed only on a proposal which finished voting and it's
   140  vote was approved. User can reply only on the latest author update. When a 
   141  proposal billing status is set to closed or completed it's not possible to 
   142  post author updates or to reply on them.
   143  
   144  Arguments:
   145  1. token     (string, required)  Proposal censorship token.
   146  2. comment   (string, required)  Comment text.
   147  3. parentid  (uint32, optional)  ID of parent commment. Including a parent ID
   148                                   indicates that the comment is a reply.
   149  
   150  Flags:
   151    --unvetted    (bool, optional)   Record is unvetted.
   152    --updatetitle (string, optional) Authour update title.
   153  `