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"`
Replyable bool `json:"replyable"`
Sensitive bool `json:"sensitive"`
Poll *CreateStatusPollForm `json:"poll,omitempty"`
ContentType model.StatusContentType `json:"content_type"`
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) {
data, err := json.Marshal(form)
if err != nil {

View file

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

View file

@ -17,21 +17,26 @@ import (
type CreateExecutor struct {
*flag.FlagSet
topLevelFlags TopLevelFlags
boostable bool
federated bool
likeable bool
replyable bool
sensitive *bool
content string
contentType string
fromFile string
language string
spoilerText string
resourceType string
listTitle string
listRepliesPolicy string
visibility string
topLevelFlags TopLevelFlags
addPoll bool
boostable bool
federated bool
likeable bool
pollAllowMultipleChoices bool
pollHideVoteCounts bool
replyable bool
sensitive *bool
content string
contentType string
fromFile string
language string
resourceType string
listTitle string
listRepliesPolicy string
spoilerText string
visibility string
pollExpiresIn TimeDurationFlagValue
pollOptions MultiStringFlagValue
}
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.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.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.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")
@ -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.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.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 {
boolVal, err := strconv.ParseBool(value)
@ -189,6 +199,22 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
Replyable: c.replyable,
Sensitive: sensitive,
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)

View file

@ -32,7 +32,11 @@ type UnsupportedAddOperationError struct {
}
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 {
@ -41,7 +45,11 @@ type UnsupportedRemoveOperationError struct {
}
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 {
@ -67,10 +75,10 @@ func (e UnknownCommandError) Error() string {
return "unknown command '" + e.Command + "'"
}
type ExpiredPollError struct{}
type PollClosedError struct{}
func (e ExpiredPollError) Error() string {
return "this poll has expired"
func (e PollClosedError) Error() string {
return "this poll is closed"
}
type MultipleChoiceError struct{}
@ -78,3 +86,11 @@ type MultipleChoiceError struct{}
func (e MultipleChoiceError) Error() string {
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,70 +8,76 @@ import (
"fmt"
"strconv"
"strings"
"time"
)
const (
flagAccountName = "account-name"
flagBrowser = "browser"
flagChoose = "choose"
flagContentType = "content-type"
flagContent = "content"
flagEnableFederation = "enable-federation"
flagEnableLikes = "enable-likes"
flagEnableReplies = "enable-replies"
flagEnableReposts = "enable-reposts"
flagFrom = "from"
flagFromFile = "from-file"
flagFull = "full"
flagInstance = "instance"
flagLanguage = "language"
flagLimit = "limit"
flagListID = "list-id"
flagListTitle = "list-title"
flagListRepliesPolicy = "list-replies-policy"
flagMyAccount = "my-account"
flagNotify = "notify"
flagPollID = "poll-id"
flagSensitive = "sensitive"
flagSkipRelationship = "skip-relationship"
flagShowPreferences = "show-preferences"
flagShowReposts = "show-reposts"
flagSpoilerText = "spoiler-text"
flagStatusID = "status-id"
flagTag = "tag"
flagTimelineCategory = "timeline-category"
flagTo = "to"
flagType = "type"
flagVisibility = "visibility"
flagAddPoll = "add-poll"
flagAccountName = "account-name"
flagBrowser = "browser"
flagChoose = "choose"
flagContentType = "content-type"
flagContent = "content"
flagEnableFederation = "enable-federation"
flagEnableLikes = "enable-likes"
flagEnableReplies = "enable-replies"
flagEnableReposts = "enable-reposts"
flagFrom = "from"
flagFromFile = "from-file"
flagFull = "full"
flagInstance = "instance"
flagLanguage = "language"
flagLimit = "limit"
flagListID = "list-id"
flagListTitle = "list-title"
flagListRepliesPolicy = "list-replies-policy"
flagMyAccount = "my-account"
flagNotify = "notify"
flagPollAllowsMultipleChoices = "poll-allows-multiple-choices"
flagPollExpiresIn = "poll-expires-in"
flagPollHidesVoteCounts = "poll-hides-vote-counts"
flagPollID = "poll-id"
flagPollOption = "poll-option"
flagSensitive = "sensitive"
flagSkipRelationship = "skip-relationship"
flagShowPreferences = "show-preferences"
flagShowReposts = "show-reposts"
flagSpoilerText = "spoiler-text"
flagStatusID = "status-id"
flagTag = "tag"
flagTimelineCategory = "timeline-category"
flagTo = "to"
flagType = "type"
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 {
ConfigDir string
NoColor *bool
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: "
for ind, vote := range *p {
if ind == len(*p)-1 {
for ind, vote := range *v {
if ind == len(*v)-1 {
value += strconv.Itoa(vote)
} else {
value += strconv.Itoa(vote) + ", "
@ -81,13 +87,32 @@ func (p *PollChoices) String() string {
return value
}
func (p *PollChoices) Set(text string) error {
func (v *MultiIntFlagValue) Set(text string) error {
value, err := strconv.Atoi(text)
if err != nil {
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
}

View file

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