add support for creating a status with a poll

This commit is contained in:
Dan Anglin 2024-06-15 01:57:10 +01:00
parent b06e92f26b
commit f66e34cd49
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
6 changed files with 153 additions and 78 deletions

View file

@ -38,10 +38,18 @@ type CreateStatusForm struct {
Likeable bool `json:"likeable"` Likeable bool `json:"likeable"`
Replyable bool `json:"replyable"` Replyable bool `json:"replyable"`
Sensitive bool `json:"sensitive"` Sensitive bool `json:"sensitive"`
Poll *CreateStatusPollForm `json:"poll,omitempty"`
ContentType model.StatusContentType `json:"content_type"` ContentType model.StatusContentType `json:"content_type"`
Visibility model.StatusVisibility `json:"visibility"` Visibility model.StatusVisibility `json:"visibility"`
} }
type CreateStatusPollForm struct {
Options []string `json:"options"`
ExpiresIn int `json:"expires_in"`
Multiple bool `json:"multiple"`
HideTotals bool `json:"hide_totals"`
}
func (g *Client) CreateStatus(form CreateStatusForm) (model.Status, error) { func (g *Client) CreateStatus(form CreateStatusForm) (model.Status, error) {
data, err := json.Marshal(form) data, err := json.Marshal(form)
if err != nil { if err != nil {

View file

@ -21,8 +21,8 @@ type AddExecutor struct {
listID string listID string
statusID string statusID string
pollID string pollID string
choices PollChoices choices MultiIntFlagValue
accountNames AccountNames accountNames MultiStringFlagValue
content string content string
} }
@ -31,7 +31,7 @@ func NewAddExecutor(tlf TopLevelFlags, name, summary string) *AddExecutor {
addExe := AddExecutor{ addExe := AddExecutor{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError), FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
accountNames: AccountNames(emptyArr), accountNames: MultiStringFlagValue(emptyArr),
topLevelFlags: tlf, topLevelFlags: tlf,
} }
@ -265,7 +265,7 @@ func (a *AddExecutor) addVoteToPoll(gtsClient *client.Client) error {
} }
if poll.Expired { if poll.Expired {
return ExpiredPollError{} return PollClosedError{}
} }
if !poll.Multiple && len(a.choices) > 1 { if !poll.Multiple && len(a.choices) > 1 {

View file

@ -18,20 +18,25 @@ type CreateExecutor struct {
*flag.FlagSet *flag.FlagSet
topLevelFlags TopLevelFlags topLevelFlags TopLevelFlags
addPoll bool
boostable bool boostable bool
federated bool federated bool
likeable bool likeable bool
pollAllowMultipleChoices bool
pollHideVoteCounts bool
replyable bool replyable bool
sensitive *bool sensitive *bool
content string content string
contentType string contentType string
fromFile string fromFile string
language string language string
spoilerText string
resourceType string resourceType string
listTitle string listTitle string
listRepliesPolicy string listRepliesPolicy string
spoilerText string
visibility string visibility string
pollExpiresIn TimeDurationFlagValue
pollOptions MultiStringFlagValue
} }
func NewCreateExecutor(tlf TopLevelFlags, name, summary string) *CreateExecutor { func NewCreateExecutor(tlf TopLevelFlags, name, summary string) *CreateExecutor {
@ -45,6 +50,9 @@ func NewCreateExecutor(tlf TopLevelFlags, name, summary string) *CreateExecutor
createExe.BoolVar(&createExe.federated, flagEnableFederation, true, "Specify if the status can be federated beyond the local timelines") createExe.BoolVar(&createExe.federated, flagEnableFederation, true, "Specify if the status can be federated beyond the local timelines")
createExe.BoolVar(&createExe.likeable, flagEnableLikes, true, "Specify if the status can be liked/favourited") createExe.BoolVar(&createExe.likeable, flagEnableLikes, true, "Specify if the status can be liked/favourited")
createExe.BoolVar(&createExe.replyable, flagEnableReplies, true, "Specify if the status can be replied to") createExe.BoolVar(&createExe.replyable, flagEnableReplies, true, "Specify if the status can be replied to")
createExe.BoolVar(&createExe.pollAllowMultipleChoices, flagPollAllowsMultipleChoices, false, "The poll allows viewers to make multiple choices in the poll")
createExe.BoolVar(&createExe.pollHideVoteCounts, flagPollHidesVoteCounts, false, "The poll will hide the vote count until it is closed")
createExe.BoolVar(&createExe.addPoll, flagAddPoll, false, "Add a poll to the status")
createExe.StringVar(&createExe.content, flagContent, "", "The content of the status to create") createExe.StringVar(&createExe.content, flagContent, "", "The content of the status to create")
createExe.StringVar(&createExe.contentType, flagContentType, "plain", "The type that the contents should be parsed from (valid values are plain and markdown)") createExe.StringVar(&createExe.contentType, flagContentType, "plain", "The type that the contents should be parsed from (valid values are plain and markdown)")
createExe.StringVar(&createExe.fromFile, flagFromFile, "", "The file path where to read the contents from") createExe.StringVar(&createExe.fromFile, flagFromFile, "", "The file path where to read the contents from")
@ -54,6 +62,8 @@ func NewCreateExecutor(tlf TopLevelFlags, name, summary string) *CreateExecutor
createExe.StringVar(&createExe.resourceType, flagType, "", "Specify the type of resource to create") createExe.StringVar(&createExe.resourceType, flagType, "", "Specify the type of resource to create")
createExe.StringVar(&createExe.listTitle, flagListTitle, "", "Specify the title of the list") createExe.StringVar(&createExe.listTitle, flagListTitle, "", "Specify the title of the list")
createExe.StringVar(&createExe.listRepliesPolicy, flagListRepliesPolicy, "list", "Specify the policy of the replies for this list (valid values are followed, list and none)") createExe.StringVar(&createExe.listRepliesPolicy, flagListRepliesPolicy, "list", "Specify the policy of the replies for this list (valid values are followed, list and none)")
createExe.Var(&createExe.pollOptions, flagPollOption, "A poll option. Use this multiple times to set multiple options")
createExe.Var(&createExe.pollExpiresIn, flagPollExpiresIn, "The duration in which the poll is open for")
createExe.BoolFunc(flagSensitive, "Specify if the status should be marked as sensitive", func(value string) error { createExe.BoolFunc(flagSensitive, "Specify if the status should be marked as sensitive", func(value string) error {
boolVal, err := strconv.ParseBool(value) boolVal, err := strconv.ParseBool(value)
@ -189,6 +199,22 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
Replyable: c.replyable, Replyable: c.replyable,
Sensitive: sensitive, Sensitive: sensitive,
Visibility: parsedVisibility, Visibility: parsedVisibility,
Poll: nil,
}
if c.addPoll {
if len(c.pollOptions) == 0 {
return NoPollOptionError{}
}
poll := client.CreateStatusPollForm{
Options: c.pollOptions,
Multiple: c.pollAllowMultipleChoices,
HideTotals: c.pollHideVoteCounts,
ExpiresIn: int(c.pollExpiresIn.Duration.Seconds()),
}
form.Poll = &poll
} }
status, err := gtsClient.CreateStatus(form) status, err := gtsClient.CreateStatus(form)

View file

@ -32,7 +32,11 @@ type UnsupportedAddOperationError struct {
} }
func (e UnsupportedAddOperationError) Error() string { func (e UnsupportedAddOperationError) Error() string {
return "adding '" + e.ResourceType + "' to '" + e.AddToResourceType + "' is not supported" return "adding '" +
e.ResourceType +
"' to '" +
e.AddToResourceType +
"' is not supported"
} }
type UnsupportedRemoveOperationError struct { type UnsupportedRemoveOperationError struct {
@ -41,7 +45,11 @@ type UnsupportedRemoveOperationError struct {
} }
func (e UnsupportedRemoveOperationError) Error() string { func (e UnsupportedRemoveOperationError) Error() string {
return "removing '" + e.ResourceType + "' from '" + e.RemoveFromResourceType + "' is not supported" return "removing '" +
e.ResourceType +
"' from '" +
e.RemoveFromResourceType +
"' is not supported"
} }
type EmptyContentError struct { type EmptyContentError struct {
@ -67,10 +75,10 @@ func (e UnknownCommandError) Error() string {
return "unknown command '" + e.Command + "'" return "unknown command '" + e.Command + "'"
} }
type ExpiredPollError struct{} type PollClosedError struct{}
func (e ExpiredPollError) Error() string { func (e PollClosedError) Error() string {
return "this poll has expired" return "this poll is closed"
} }
type MultipleChoiceError struct{} type MultipleChoiceError struct{}
@ -78,3 +86,11 @@ type MultipleChoiceError struct{}
func (e MultipleChoiceError) Error() string { func (e MultipleChoiceError) Error() string {
return "this poll does not allow multiple choices" return "this poll does not allow multiple choices"
} }
type NoPollOptionError struct{}
func (e NoPollOptionError) Error() string {
return "no options were provided for this poll, please use the --" +
flagPollOption +
" flag to add options to the poll"
}

View file

@ -8,9 +8,11 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"time"
) )
const ( const (
flagAddPoll = "add-poll"
flagAccountName = "account-name" flagAccountName = "account-name"
flagBrowser = "browser" flagBrowser = "browser"
flagChoose = "choose" flagChoose = "choose"
@ -31,7 +33,11 @@ const (
flagListRepliesPolicy = "list-replies-policy" flagListRepliesPolicy = "list-replies-policy"
flagMyAccount = "my-account" flagMyAccount = "my-account"
flagNotify = "notify" flagNotify = "notify"
flagPollAllowsMultipleChoices = "poll-allows-multiple-choices"
flagPollExpiresIn = "poll-expires-in"
flagPollHidesVoteCounts = "poll-hides-vote-counts"
flagPollID = "poll-id" flagPollID = "poll-id"
flagPollOption = "poll-option"
flagSensitive = "sensitive" flagSensitive = "sensitive"
flagSkipRelationship = "skip-relationship" flagSkipRelationship = "skip-relationship"
flagShowPreferences = "show-preferences" flagShowPreferences = "show-preferences"
@ -45,33 +51,33 @@ const (
flagVisibility = "visibility" flagVisibility = "visibility"
) )
type AccountNames []string
func (a *AccountNames) String() string {
return strings.Join(*a, ", ")
}
func (a *AccountNames) Set(value string) error {
if len(value) > 0 {
*a = append(*a, value)
}
return nil
}
type TopLevelFlags struct { type TopLevelFlags struct {
ConfigDir string ConfigDir string
NoColor *bool NoColor *bool
Pager string Pager string
} }
type PollChoices []int type MultiStringFlagValue []string
func (p *PollChoices) String() string { func (v *MultiStringFlagValue) String() string {
return strings.Join(*v, ", ")
}
func (v *MultiStringFlagValue) Set(value string) error {
if len(value) > 0 {
*v = append(*v, value)
}
return nil
}
type MultiIntFlagValue []int
func (v *MultiIntFlagValue) String() string {
value := "Choices: " value := "Choices: "
for ind, vote := range *p { for ind, vote := range *v {
if ind == len(*p)-1 { if ind == len(*v)-1 {
value += strconv.Itoa(vote) value += strconv.Itoa(vote)
} else { } else {
value += strconv.Itoa(vote) + ", " value += strconv.Itoa(vote) + ", "
@ -81,13 +87,32 @@ func (p *PollChoices) String() string {
return value return value
} }
func (p *PollChoices) Set(text string) error { func (v *MultiIntFlagValue) Set(text string) error {
value, err := strconv.Atoi(text) value, err := strconv.Atoi(text)
if err != nil { if err != nil {
return fmt.Errorf("unable to parse the value to an integer: %w", err) return fmt.Errorf("unable to parse the value to an integer: %w", err)
} }
*p = append(*p, value) *v = append(*v, value)
return nil
}
type TimeDurationFlagValue struct {
Duration time.Duration
}
func (v TimeDurationFlagValue) String() string {
return ""
}
func (v *TimeDurationFlagValue) Set(text string) error {
duration, err := time.ParseDuration(text)
if err != nil {
return fmt.Errorf("unable to parse the value as time duration: %w", err)
}
v.Duration = duration
return nil return nil
} }

View file

@ -19,7 +19,7 @@ type RemoveExecutor struct {
fromResourceType string fromResourceType string
listID string listID string
statusID string statusID string
accountNames AccountNames accountNames MultiStringFlagValue
} }
func NewRemoveExecutor(tlf TopLevelFlags, name, summary string) *RemoveExecutor { func NewRemoveExecutor(tlf TopLevelFlags, name, summary string) *RemoveExecutor {
@ -27,7 +27,7 @@ func NewRemoveExecutor(tlf TopLevelFlags, name, summary string) *RemoveExecutor
removeExe := RemoveExecutor{ removeExe := RemoveExecutor{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError), FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
accountNames: AccountNames(emptyArr), accountNames: MultiStringFlagValue(emptyArr),
topLevelFlags: tlf, topLevelFlags: tlf,
} }