--- a/cmd/accounts.go Mon Jun 11 06:38:55 2018 +0200
+++ b/cmd/accounts.go Thu Sep 06 01:10:15 2018 +0200
@@ -22,24 +22,28 @@
var accountsOpts struct {
accountID int64
accountUID string
- unset bool // TODO remove eventually?
- limit, keep uint // Limit the results
- sinceID, maxID int64 // Query boundaries
- all bool // Try to fetch all results
- onlyMedia, onlyPinned bool // For acccount statuses
- excludeReplies bool // For acccount statuses
- remoteUID string // For account follow
- reblogs bool // For account follow
- acceptFR, rejectFR bool // For account follow_requests
- list bool // For account follow_requests/reports
- accountIDs string // For account relationships
- statusIDs string // For account reports
- comment string // For account reports
- displayName, note string // For account update
- avatar, header string // For account update
- locked bool // For account update
- muteNotifications bool // For account mute
- following bool // For account search
+ unset bool // TODO remove eventually?
+ limit, keep uint // Limit the results
+ sinceID, maxID int64 // Query boundaries
+ all bool // Try to fetch all results
+ onlyMedia, onlyPinned bool // For acccount statuses
+ excludeReplies bool // For acccount statuses
+ remoteUID string // For account follow
+ reblogs bool // For account follow
+ acceptFR, rejectFR bool // For account follow_requests
+ list bool // For account follow_requests/reports
+ accountIDs string // For account relationships
+ statusIDs string // For account reports
+ comment string // For account reports
+ displayName, note string // For account update
+ profileFields []string // For account update
+ avatar, header string // For account update
+ defaultLanguage string // For account update
+ defaultPrivacy string // For account update
+ defaultSensitive bool // For account update
+ locked, bot bool // For account update
+ muteNotifications bool // For account mute
+ following bool // For account search
}
func init() {
@@ -86,7 +90,12 @@
accountUpdateSubcommand.Flags().StringVar(&accountsOpts.note, "note", "", "User note (a.k.a. bio)")
accountUpdateSubcommand.Flags().StringVar(&accountsOpts.avatar, "avatar", "", "User avatar image")
accountUpdateSubcommand.Flags().StringVar(&accountsOpts.header, "header", "", "User header image")
+ accountUpdateSubcommand.Flags().StringArrayVar(&accountsOpts.profileFields, "profile-field", nil, "Profile metadata field (NAME=VALUE)")
+ accountUpdateSubcommand.Flags().StringVar(&accountsOpts.defaultLanguage, "default-language", "", "Default toots language (iso 639 code)")
+ accountUpdateSubcommand.Flags().StringVar(&accountsOpts.defaultPrivacy, "default-privacy", "", "Default toot privacy (public, unlisted, private)")
+ accountUpdateSubcommand.Flags().BoolVar(&accountsOpts.defaultSensitive, "default-sensitive", false, "Mark medias as sensitive by default")
accountUpdateSubcommand.Flags().BoolVar(&accountsOpts.locked, "locked", false, "Following account requires approval")
+ accountUpdateSubcommand.Flags().BoolVar(&accountsOpts.bot, "bot", false, "Set as service (automated) account")
// Those variables will be used to check if the options were
// explicitly set or not
@@ -170,9 +179,12 @@
accountFollowSubcommand,
accountBlockSubcommand,
accountMuteSubcommand,
+ accountPinSubcommand,
+ accountUnpinSubcommand,
accountRelationshipsSubcommand,
accountReportsSubcommand,
accountUpdateSubcommand,
+ accountListEndorsementsSubcommand,
}
var accountSearchSubcommand = &cobra.Command{
@@ -246,6 +258,33 @@
},
}
+var accountPinSubcommand = &cobra.Command{
+ Use: "pin",
+ Short: "Endorse (pin) the account",
+ Aliases: []string{"endorse"},
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return accountSubcommandsRunE(cmd.Name(), args)
+ },
+}
+
+var accountUnpinSubcommand = &cobra.Command{
+ Use: "unpin",
+ Short: "Cancel endorsement of an account",
+ Aliases: []string{"disavow"},
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return accountSubcommandsRunE(cmd.Name(), args)
+ },
+}
+
+var accountListEndorsementsSubcommand = &cobra.Command{
+ Use: "pinned",
+ Short: `Display the list of pinned (endorsed) accounts`,
+ Aliases: []string{"list-endorsements", "get-endorsements"},
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return accountSubcommandsRunE(cmd.Name(), args)
+ },
+}
+
var accountRelationshipsSubcommand = &cobra.Command{
Use: "relationships --account-ids ACC1,ACC2...",
Short: "List relationships with the accounts",
@@ -364,7 +403,7 @@
switch subcmd {
case "show", "search", "update":
// These subcommands do not require an account ID
- case "favourites", "blocks", "mutes":
+ case "favourites", "blocks", "mutes", "pinned":
// Those subcommands can not use an account ID
if opt.accountID > 0 {
return errors.New("useless account ID")
@@ -564,6 +603,14 @@
relationship, err = gClient.MuteAccount(opt.accountID, muteNotif)
}
obj = relationship
+ case "pin", "unpin":
+ var relationship *madon.Relationship
+ if subcmd == "unpin" {
+ relationship, err = gClient.UnpinAccount(opt.accountID)
+ } else {
+ relationship, err = gClient.PinAccount(opt.accountID)
+ }
+ obj = relationship
case "favourites":
var statusList []madon.Status
statusList, err = gClient.GetFavourites(limOpts)
@@ -585,6 +632,13 @@
accountList = accountList[:opt.keep]
}
obj = accountList
+ case "pinned":
+ var accountList []madon.Account
+ accountList, err = gClient.GetEndorsements(limOpts)
+ if opt.keep > 0 && len(accountList) > int(opt.keep) {
+ accountList = accountList[:opt.keep]
+ }
+ obj = accountList
case "relationships":
var ids []int64
ids, err = splitIDs(opt.accountIDs)
@@ -623,27 +677,65 @@
report, err = gClient.ReportUser(opt.accountID, ids, opt.comment)
obj = report
case "update":
- var dn, note, avatar, header *string
- var locked *bool
+ var updateParams madon.UpdateAccountParams
+ var source *madon.SourceParams
change := false
+
if accountUpdateFlags.Lookup("display-name").Changed {
- dn = &opt.displayName
+ updateParams.DisplayName = &opt.displayName
change = true
}
if accountUpdateFlags.Lookup("note").Changed {
- note = &opt.note
+ updateParams.Note = &opt.note
change = true
}
if accountUpdateFlags.Lookup("avatar").Changed {
- avatar = &opt.avatar
+ updateParams.AvatarImagePath = &opt.avatar
change = true
}
if accountUpdateFlags.Lookup("header").Changed {
- header = &opt.header
+ updateParams.HeaderImagePath = &opt.header
change = true
}
if accountUpdateFlags.Lookup("locked").Changed {
- locked = &opt.locked
+ updateParams.Locked = &opt.locked
+ change = true
+ }
+ if accountUpdateFlags.Lookup("bot").Changed {
+ updateParams.Bot = &opt.bot
+ change = true
+ }
+ if accountUpdateFlags.Lookup("default-language").Changed {
+ if source == nil {
+ source = &madon.SourceParams{}
+ }
+ source.Language = &opt.defaultLanguage
+ change = true
+ }
+ if accountUpdateFlags.Lookup("default-privacy").Changed {
+ if source == nil {
+ source = &madon.SourceParams{}
+ }
+ source.Privacy = &opt.defaultPrivacy
+ change = true
+ }
+ if accountUpdateFlags.Lookup("default-sensitive").Changed {
+ if source == nil {
+ source = &madon.SourceParams{}
+ }
+ source.Sensitive = &opt.defaultSensitive
+ change = true
+ }
+ if accountUpdateFlags.Lookup("profile-field").Changed {
+ var fa = []madon.Field{}
+ for _, f := range opt.profileFields {
+ kv := strings.SplitN(f, "=", 2)
+ if len(kv) != 2 {
+ return errors.New("cannot parse field")
+ }
+ fa = append(fa, madon.Field{Name: kv[0], Value: kv[1]})
+ }
+ updateParams.FieldsAttributes = &fa
change = true
}
@@ -651,8 +743,10 @@
return errors.New("missing parameters")
}
+ updateParams.Source = source
+
var account *madon.Account
- account, err = gClient.UpdateAccount(dn, note, avatar, header, locked)
+ account, err = gClient.UpdateAccount(updateParams)
obj = account
default:
return errors.New("accountSubcommand: internal error")
--- a/cmd/status.go Mon Jun 11 06:38:55 2018 +0200
+++ b/cmd/status.go Thu Sep 06 01:10:15 2018 +0200
@@ -194,7 +194,9 @@
Use: "post",
Aliases: []string{"toot", "pouet"},
Short: "Post a message (same as 'madonctl toot')",
- Example: ` madonctl status post --spoiler Warning "Hello, World"
+ Example: ` madonctl status post "Hello, World"
+ madonctl status post --spoiler Warning "Spoiled"
+ madonctl status toot --visibility private "To my followers only"
madonctl status toot --sensitive --file image.jpg Image
madonctl status post --media-ids ID1,ID2,ID3 Image
madonctl status toot --text-file message.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/suggestions.go Thu Sep 06 01:10:15 2018 +0200
@@ -0,0 +1,156 @@
+// Copyright © 2018 Mikael Berthe <mikael@lilotux.net>
+//
+// Licensed under the MIT license.
+// Please see the LICENSE file is this directory.
+
+package cmd
+
+import (
+ "os"
+
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+
+ "github.com/McKael/madon"
+)
+
+var suggestionsOpts struct {
+ accountID int64
+ accountIDs string
+
+ //limit uint
+ keep uint
+ //all bool
+}
+
+//suggestionsCmd represents the suggestions command
+var suggestionsCmd = &cobra.Command{
+ Use: "suggestions",
+ Aliases: []string{"suggestion"},
+ Short: "Display and remove the follow suggestions",
+ RunE: suggestionsGetRunE, // Defaults to list
+}
+
+func init() {
+ RootCmd.AddCommand(suggestionsCmd)
+
+ // Subcommands
+ suggestionsCmd.AddCommand(suggestionsSubcommands...)
+
+ //suggestionsGetSubcommand.Flags().UintVarP(&suggestionsOpts.limit, "limit", "l", 0, "Limit number of API results")
+ suggestionsGetSubcommand.Flags().UintVarP(&suggestionsOpts.keep, "keep", "k", 0, "Limit number of results")
+ //suggestionsGetSubcommand.Flags().BoolVar(&suggestionsOpts.all, "all", false, "Fetch all results")
+
+ suggestionsDeleteSubcommand.Flags().Int64VarP(&suggestionsOpts.accountID, "account-id", "a", 0, "Account ID number")
+ suggestionsDeleteSubcommand.Flags().StringVar(&suggestionsOpts.accountIDs, "account-ids", "", "Comma-separated list of account IDs")
+}
+
+var suggestionsSubcommands = []*cobra.Command{
+ suggestionsGetSubcommand,
+ suggestionsDeleteSubcommand,
+}
+
+var suggestionsGetSubcommand = &cobra.Command{
+ Use: "list",
+ Short: "Display the suggestions (default subcommand)",
+ Long: `Display the list of account suggestions.`,
+ Aliases: []string{"ls", "get", "display", "show"},
+ RunE: suggestionsGetRunE,
+}
+
+var suggestionsDeleteSubcommand = &cobra.Command{
+ Use: "delete",
+ Short: "Remove an account from the suggestion list",
+ Aliases: []string{"remove", "del", "rm"},
+ RunE: suggestionsDeleteRunE,
+}
+
+func suggestionsGetRunE(cmd *cobra.Command, args []string) error {
+ opt := suggestionsOpts
+
+ /*
+ // Note: The API currently does not support pagination
+ // Set up LimitParams
+ var limOpts *madon.LimitParams
+ if opt.all || opt.limit > 0 {
+ limOpts = new(madon.LimitParams)
+ limOpts.All = opt.all
+ }
+ if opt.limit > 0 {
+ limOpts.Limit = int(opt.limit)
+ }
+ */
+
+ // We need to be logged in
+ if err := madonInit(true); err != nil {
+ return err
+ }
+
+ var obj interface{}
+ var err error
+
+ var accountList []madon.Account
+ accountList, err = gClient.GetSuggestions(nil)
+
+ if opt.keep > 0 && len(accountList) > int(opt.keep) {
+ accountList = accountList[:opt.keep]
+ }
+
+ obj = accountList
+
+ if err != nil {
+ errPrint("Error: %s", err.Error())
+ os.Exit(1)
+ }
+ if obj == nil {
+ return nil
+ }
+
+ p, err := getPrinter()
+ if err != nil {
+ errPrint("Error: %v", err)
+ os.Exit(1)
+ }
+ return p.printObj(obj)
+}
+
+func suggestionsDeleteRunE(cmd *cobra.Command, args []string) error {
+ opt := suggestionsOpts
+ var ids []int64
+ var err error
+
+ if opt.accountID < 1 && len(opt.accountIDs) == 0 {
+ return errors.New("missing account IDs")
+ }
+ if opt.accountID > 0 && len(opt.accountIDs) > 0 {
+ return errors.New("incompatible options")
+ }
+
+ ids, err = splitIDs(opt.accountIDs)
+ if err != nil {
+ return errors.New("cannot parse account IDs")
+ }
+ if opt.accountID > 0 { // Allow --account-id
+ ids = []int64{opt.accountID}
+ }
+ if len(ids) < 1 {
+ return errors.New("missing account IDs")
+ }
+
+ // We need to be logged in
+ if err := madonInit(true); err != nil {
+ return err
+ }
+
+ for _, id := range ids {
+ if e := gClient.DeleteSuggestion(id); err != nil {
+ errPrint("Cannot remove account %d: %s", id, e)
+ err = e
+ }
+ }
+
+ if err != nil {
+ os.Exit(1)
+ }
+ return nil
+}
--- a/cmd/toot.go Mon Jun 11 06:38:55 2018 +0200
+++ b/cmd/toot.go Thu Sep 06 01:10:15 2018 +0200
@@ -49,6 +49,7 @@
Aliases: []string{"post", "pouet"},
Short: "Post a message (toot)",
Example: ` madonctl toot message
+ madonctl toot --visibility private "To my followers only"
madonctl toot --spoiler Warning "Hello, World"
madonctl status post --media-ids ID1,ID2 "Here are the photos"
madonctl post --sensitive --file image.jpg Image
@@ -147,7 +148,15 @@
}
}
- return gClient.PostStatus(tootText, opt.inReplyToID, ids, opt.sensitive, opt.spoiler, opt.visibility)
+ postParam := madon.PostStatusParams{
+ Text: tootText,
+ InReplyTo: opt.inReplyToID,
+ MediaIDs: ids,
+ Sensitive: opt.sensitive,
+ SpoilerText: opt.spoiler,
+ Visibility: opt.visibility,
+ }
+ return gClient.PostStatus(postParam)
}
func mentionsList(s *madon.Status) (string, error) {
--- a/cmd/version.go Mon Jun 11 06:38:55 2018 +0200
+++ b/cmd/version.go Thu Sep 06 01:10:15 2018 +0200
@@ -23,7 +23,7 @@
}
// VERSION of the madonctl application
-var VERSION = "2.3.1"
+var VERSION = "2.4.0-dev"
var versionCmd = &cobra.Command{
Use: "version",
--- a/printer/plainprinter.go Mon Jun 11 06:38:55 2018 +0200
+++ b/printer/plainprinter.go Thu Sep 06 01:10:15 2018 +0200
@@ -288,6 +288,7 @@
indentedPrint(w, indent, false, false, "Blocking", "%v", r.Blocking)
indentedPrint(w, indent, false, false, "Muting", "%v", r.Muting)
indentedPrint(w, indent, false, false, "Muting notifications", "%v", r.MutingNotifications)
+ indentedPrint(w, indent, false, false, "Endorsed", "%v", r.Endorsed)
indentedPrint(w, indent, false, false, "Requested", "%v", r.Requested)
return nil
}
@@ -312,7 +313,8 @@
if len(r.Hashtags) > 0 {
indentedPrint(w, indent, false, false, "Hashtags", "")
for _, tag := range r.Hashtags {
- indentedPrint(w, indent+p.Indent, true, false, "Tag", "%s", tag)
+ indentedPrint(w, indent+p.Indent, true, false, "Tag", "%s", tag.Name)
+ indentedPrint(w, indent+p.Indent, false, true, "URL", "%s", tag.URL)
}
}
return nil
--- a/templates/themes/ansi-dark/relationship.tmpl Mon Jun 11 06:38:55 2018 +0200
+++ b/templates/themes/ansi-dark/relationship.tmpl Thu Sep 06 01:10:15 2018 +0200
@@ -1,8 +1,10 @@
- Account ID: {{color "red"}}{{.id}}{{color "reset"}}
Following: {{.following}}
-{{- if .showing_reblogs}} Showing boosts: {{.showing_reblogs}}{{end}}
+{{- if .showing_reblogs}}
+ Showing boosts: {{.showing_reblogs}}{{end}}
Followed-by: {{.followed_by}}
Blocking: {{.blocking}}
Muting: {{.muting}}
Muting notif.: {{.muting}}
+ Endorsed: {{.endorsed}}
Requested: {{.requested}}
--- a/templates/themes/ansi-dark/results.tmpl Mon Jun 11 06:38:55 2018 +0200
+++ b/templates/themes/ansi-dark/results.tmpl Thu Sep 06 01:10:15 2018 +0200
@@ -33,5 +33,5 @@
Remote URL: {{.remote_url}}{{end}}{{end}}{{end}}{{/* of range */}}{{end}}{{/* of statuses */}}
{{- with .hashtags}}{{color ",,bold"}}Hashtags:{{color "reset"}}
{{- range .}}
- - {{.}}{{end}}{{/* of range */}}
+ - Tag: {{.name}}{{end}}{{/* of range */}}
{{end}}{{/* of statuses */ -}}
--- a/templates/themes/ansi/relationship.tmpl Mon Jun 11 06:38:55 2018 +0200
+++ b/templates/themes/ansi/relationship.tmpl Thu Sep 06 01:10:15 2018 +0200
@@ -1,8 +1,10 @@
- Account ID: {{color "red"}}{{.id}}{{color "reset"}}
Following: {{.following}}
-{{- if .showing_reblogs}} Showing boosts: {{.showing_reblogs}}{{end}}
+{{- if .showing_reblogs}}
+ Showing boosts: {{.showing_reblogs}}{{end}}
Followed-by: {{.followed_by}}
Blocking: {{.blocking}}
Muting: {{.muting}}
Muting notif.: {{.muting}}
+ Endorsed: {{.endorsed}}
Requested: {{.requested}}
--- a/templates/themes/ansi/results.tmpl Mon Jun 11 06:38:55 2018 +0200
+++ b/templates/themes/ansi/results.tmpl Thu Sep 06 01:10:15 2018 +0200
@@ -33,5 +33,5 @@
Remote URL: {{.remote_url}}{{end}}{{end}}{{end}}{{/* of range */}}{{end}}{{/* of statuses */}}
{{- with .hashtags}}{{color ",,bold"}}Hashtags:{{color "reset"}}
{{- range .}}
- - {{.}}{{end}}{{/* of range */}}
+ - Tag: {{.name}}{{end}}{{/* of range */}}
{{end}}{{/* of statuses */ -}}