Merge pull request #29 from rjp/fix/support-non-int-ids
authorMikael Berthe <mikael@lilotux.net>
Sat, 04 Feb 2023 13:17:17 +0100
changeset 268 4dd196a4ee7c
parent 266 80973a656b81 (current diff)
parent 267 5b91a65ba95a (diff)
child 269 c50e88700432
Merge pull request #29 from rjp/fix/support-non-int-ids Update to handle non-int64 IDs committer: GitHub <noreply@github.com>
cmd/accounts.go
cmd/domainblocks.go
cmd/lists.go
cmd/madon.go
cmd/media.go
cmd/notifications.go
cmd/status.go
cmd/suggestions.go
cmd/timelines.go
cmd/toot.go
printer/plainprinter.go
vendor/github.com/McKael/madon/v3/.gitignore
vendor/github.com/McKael/madon/v3/LICENSE
vendor/github.com/McKael/madon/v3/README.md
vendor/github.com/McKael/madon/v3/account.go
vendor/github.com/McKael/madon/v3/api.go
vendor/github.com/McKael/madon/v3/app.go
vendor/github.com/McKael/madon/v3/domain.go
vendor/github.com/McKael/madon/v3/emoji.go
vendor/github.com/McKael/madon/v3/endorsements.go
vendor/github.com/McKael/madon/v3/go.mod
vendor/github.com/McKael/madon/v3/instance.go
vendor/github.com/McKael/madon/v3/lists.go
vendor/github.com/McKael/madon/v3/login.go
vendor/github.com/McKael/madon/v3/madon.go
vendor/github.com/McKael/madon/v3/media.go
vendor/github.com/McKael/madon/v3/notifications.go
vendor/github.com/McKael/madon/v3/report.go
vendor/github.com/McKael/madon/v3/search.go
vendor/github.com/McKael/madon/v3/status.go
vendor/github.com/McKael/madon/v3/streams.go
vendor/github.com/McKael/madon/v3/suggestions.go
vendor/github.com/McKael/madon/v3/timelines.go
vendor/github.com/McKael/madon/v3/types.go
vendor/github.com/fsnotify/fsnotify/system_bsd.go
vendor/github.com/fsnotify/fsnotify/system_darwin.go
vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go
vendor/github.com/pelletier/go-toml/v2/unstable/ast.go
vendor/github.com/pelletier/go-toml/v2/unstable/kind.go
vendor/github.com/pelletier/go-toml/v2/unstable/parser.go
vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go
vendor/github.com/spf13/viper/watch_unsupported.go
vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s
vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go
vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go
vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go
vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go
vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s
vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go
vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s
vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go
vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s
vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go
vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go
vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go
vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go
vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go
vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go
vendor/golang.org/x/sys/windows/aliases.go
vendor/golang.org/x/sys/windows/race.go
vendor/golang.org/x/sys/windows/race0.go
vendor/golang.org/x/sys/windows/syscall.go
--- a/cmd/accounts.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/accounts.go	Sat Feb 04 13:17:17 2023 +0100
@@ -20,30 +20,30 @@
 var accountUpdateFlags, accountMuteFlags, accountFollowFlags *flag.FlagSet
 
 var accountsOpts struct {
-	accountID             int64
+	accountID             madon.ActivityID
 	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
-	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
+	unset                 bool             // TODO remove eventually?
+	limit, keep           uint             // Limit the results
+	sinceID, maxID        madon.ActivityID // 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() {
@@ -53,12 +53,12 @@
 	accountsCmd.AddCommand(accountSubcommands...)
 
 	// Global flags
-	accountsCmd.PersistentFlags().Int64VarP(&accountsOpts.accountID, "account-id", "a", 0, "Account ID number")
+	accountsCmd.PersistentFlags().StringVarP(&accountsOpts.accountID, "account-id", "a", "", "Account ID number")
 	accountsCmd.PersistentFlags().StringVarP(&accountsOpts.accountUID, "user-id", "u", "", "Account user ID")
 	accountsCmd.PersistentFlags().UintVarP(&accountsOpts.limit, "limit", "l", 0, "Limit number of API results")
 	accountsCmd.PersistentFlags().UintVarP(&accountsOpts.keep, "keep", "k", 0, "Limit number of results")
-	accountsCmd.PersistentFlags().Int64Var(&accountsOpts.sinceID, "since-id", 0, "Request IDs greater than a value")
-	accountsCmd.PersistentFlags().Int64Var(&accountsOpts.maxID, "max-id", 0, "Request IDs less (or equal) than a value")
+	accountsCmd.PersistentFlags().StringVar(&accountsOpts.sinceID, "since-id", "", "Request IDs greater than a value")
+	accountsCmd.PersistentFlags().StringVar(&accountsOpts.maxID, "max-id", "", "Request IDs less (or equal) than a value")
 	accountsCmd.PersistentFlags().BoolVar(&accountsOpts.all, "all", false, "Fetch all results")
 
 	// Subcommand flags
@@ -377,7 +377,7 @@
 
 	// Check account is provided in only one way
 	aCounter := 0
-	if opt.accountID > 0 {
+	if opt.accountID != "" {
 		aCounter++
 	}
 	if opt.accountUID != "" {
@@ -396,8 +396,8 @@
 
 	if userInArg {
 		// Is the argument an account ID?
-		if n, err := strconv.ParseInt(args[0], 10, 64); err == nil {
-			opt.accountID = n
+		if _, err := strconv.ParseInt(args[0], 10, 64); err == nil {
+			opt.accountID = args[0]
 		} else if strings.HasPrefix(args[0], "https://") || strings.HasPrefix(args[0], "http://") {
 			// That is not a remote UID scheme
 			opt.accountUID = args[0]
@@ -417,7 +417,7 @@
 	}
 
 	if opt.accountUID != "" {
-		if opt.accountID > 0 {
+		if opt.accountID != "" {
 			return errors.New("cannot use both account ID and UID")
 		}
 		// Sign in early to look the user id up
@@ -426,7 +426,7 @@
 			return err
 		}
 		opt.accountID, err = accountLookupUser(opt.accountUID)
-		if err != nil || opt.accountID < 1 {
+		if err != nil || opt.accountID == "" {
 			if err != nil {
 				errPrint("Cannot find user '%s': %v", opt.accountUID, err)
 			} else {
@@ -441,18 +441,18 @@
 		// These subcommands do not require an account ID
 	case "favourites", "blocks", "mutes", "pinned":
 		// Those subcommands can not use an account ID
-		if opt.accountID > 0 {
+		if opt.accountID != "" {
 			return errors.New("useless account ID")
 		}
 	case "follow", "unfollow":
 		// We need an account ID or a remote UID
-		if opt.accountID < 1 && opt.remoteUID == "" {
+		if opt.accountID == "" && opt.remoteUID == "" {
 			return errors.New("missing account ID or URI")
 		}
-		if opt.accountID > 0 && opt.remoteUID != "" {
+		if opt.accountID != "" && opt.remoteUID != "" {
 			return errors.New("cannot use both account ID and URI")
 		}
-		if (opt.unset || subcmd == "unfollow") && opt.accountID < 1 {
+		if (opt.unset || subcmd == "unfollow") && opt.accountID == "" {
 			return errors.New("unfollowing requires an account ID")
 		}
 	case "follow-requests":
@@ -468,27 +468,27 @@
 			if opt.acceptFR && opt.rejectFR {
 				return errors.New("incompatible options")
 			}
-			if opt.accountID < 1 {
+			if opt.accountID == "" {
 				return errors.New("missing account ID")
 			}
 		}
 	case "relationships":
-		if opt.accountID < 1 && len(opt.accountIDs) == 0 {
+		if opt.accountID == "" && len(opt.accountIDs) == 0 {
 			return errors.New("missing account IDs")
 		}
-		if opt.accountID > 0 && len(opt.accountIDs) > 0 {
+		if opt.accountID != "" && len(opt.accountIDs) > 0 {
 			return errors.New("incompatible options")
 		}
 	case "reports":
 		if opt.list {
 			break // No argument needed
 		}
-		if opt.accountID < 1 || len(opt.statusIDs) == 0 || opt.comment == "" {
+		if opt.accountID == "" || len(opt.statusIDs) == 0 || opt.comment == "" {
 			return errors.New("missing parameter")
 		}
 	case "followers", "following", "statuses":
 		// If the user's account ID is missing, get it
-		if opt.accountID < 1 {
+		if opt.accountID == "" {
 			// Sign in now to look the user id up
 			if err := madonInit(true); err != nil {
 				return err
@@ -504,13 +504,13 @@
 		}
 	default:
 		// The other subcommands here require an account ID
-		if opt.accountID < 1 {
+		if opt.accountID == "" {
 			return errors.New("missing account ID")
 		}
 	}
 
 	var limOpts *madon.LimitParams
-	if opt.all || opt.limit > 0 || opt.sinceID > 0 || opt.maxID > 0 {
+	if opt.all || opt.limit > 0 || opt.sinceID != "" || opt.maxID != "" {
 		limOpts = new(madon.LimitParams)
 		limOpts.All = opt.all
 	}
@@ -518,10 +518,10 @@
 	if opt.limit > 0 {
 		limOpts.Limit = int(opt.limit)
 	}
-	if opt.maxID > 0 {
+	if opt.maxID != "" {
 		limOpts.MaxID = opt.maxID
 	}
-	if opt.sinceID > 0 {
+	if opt.sinceID != "" {
 		limOpts.SinceID = opt.sinceID
 	}
 
@@ -536,7 +536,7 @@
 	switch subcmd {
 	case "show":
 		var account *madon.Account
-		if opt.accountID > 0 {
+		if opt.accountID != "" {
 			account, err = gClient.GetAccount(opt.accountID)
 		} else {
 			account, err = gClient.GetCurrentAccount()
@@ -574,7 +574,7 @@
 			obj = relationship
 			break
 		}
-		if opt.accountID <= 0 {
+		if opt.accountID == "" {
 			if opt.remoteUID != "" {
 				// Remote account
 				var account *madon.Account
@@ -597,7 +597,7 @@
 		if opt.list {
 			var followRequests []madon.Account
 			followRequests, err = gClient.GetAccountFollowRequests(limOpts)
-			if opt.accountID > 0 { // Display a specific request
+			if opt.accountID != "" { // Display a specific request
 				var fRequest *madon.Account
 				for _, fr := range followRequests {
 					if fr.ID == opt.accountID {
@@ -676,13 +676,13 @@
 		}
 		obj = accountList
 	case "relationships":
-		var ids []int64
+		var ids []madon.ActivityID
 		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 opt.accountID != "" { // Allow --account-id
+			ids = []madon.ActivityID{opt.accountID}
 		}
 		if len(ids) < 1 {
 			return errors.New("missing account IDs")
@@ -701,7 +701,7 @@
 			break
 		}
 		// Send a report
-		var ids []int64
+		var ids []madon.ActivityID
 		ids, err = splitIDs(opt.statusIDs)
 		if err != nil {
 			return errors.New("cannot parse status IDs")
@@ -807,17 +807,17 @@
 // accountLookupUser tries to find a (single) user matching 'user'
 // If the user is an HTTP URL, it will use the search API, else
 // it will use the accounts/search API.
-func accountLookupUser(user string) (int64, error) {
-	var accID int64
+func accountLookupUser(user string) (madon.ActivityID, error) {
+	var accID madon.ActivityID
 
 	if strings.HasPrefix(user, "https://") || strings.HasPrefix(user, "http://") {
 		res, err := gClient.Search(user, true)
 		if err != nil {
-			return 0, err
+			return "", err
 		}
 		if res != nil {
 			if len(res.Accounts) > 1 {
-				return 0, errors.New("several results")
+				return "", errors.New("several results")
 			}
 			if len(res.Accounts) == 1 {
 				accID = res.Accounts[0].ID
@@ -829,7 +829,7 @@
 
 		accList, err := gClient.SearchAccounts(user, false, &madon.LimitParams{Limit: 2})
 		if err != nil {
-			return 0, err
+			return "", err
 		}
 		for _, u := range accList {
 			if u.Acct == user {
@@ -839,8 +839,8 @@
 		}
 	}
 
-	if accID < 1 {
-		return 0, errors.New("user not found")
+	if accID == "" {
+		return "", errors.New("user not found")
 	}
 	if verbose {
 		errPrint("User '%s' is account ID %d", user, user)
--- a/cmd/domainblocks.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/domainblocks.go	Sat Feb 04 13:17:17 2023 +0100
@@ -17,9 +17,9 @@
 var domainBlocksOpts struct {
 	show, block, unblock bool
 
-	limit          uint  // Limit the results
-	sinceID, maxID int64 // Query boundaries
-	all            bool  // Try to fetch all results
+	limit          uint              // Limit the results
+	sinceID, maxID madon.ActivityID // Query boundaries
+	all            bool              // Try to fetch all results
 }
 
 // timelinesCmd represents the timelines command
@@ -41,8 +41,8 @@
 	domainBlocksCmd.Flags().BoolVar(&domainBlocksOpts.unblock, "unblock", false, "Unblock domain")
 
 	domainBlocksCmd.Flags().UintVarP(&domainBlocksOpts.limit, "limit", "l", 0, "Limit number of results")
-	domainBlocksCmd.Flags().Int64Var(&domainBlocksOpts.sinceID, "since-id", 0, "Request IDs greater than a value")
-	domainBlocksCmd.Flags().Int64Var(&domainBlocksOpts.maxID, "max-id", 0, "Request IDs less (or equal) than a value")
+	domainBlocksCmd.Flags().StringVar(&domainBlocksOpts.sinceID, "since-id", "", "Request IDs greater than a value")
+	domainBlocksCmd.Flags().StringVar(&domainBlocksOpts.maxID, "max-id", "", "Request IDs less (or equal) than a value")
 	domainBlocksCmd.Flags().BoolVar(&domainBlocksOpts.all, "all", false, "Fetch all results")
 }
 
@@ -71,17 +71,17 @@
 
 	// Set up LimitParams
 	var limOpts *madon.LimitParams
-	if opt.all || opt.limit > 0 || opt.sinceID > 0 || opt.maxID > 0 {
+	if opt.all || opt.limit > 0 || opt.sinceID != "" || opt.maxID != "" {
 		limOpts = new(madon.LimitParams)
 		limOpts.All = opt.all
 	}
 	if opt.limit > 0 {
 		limOpts.Limit = int(opt.limit)
 	}
-	if opt.maxID > 0 {
+	if opt.maxID != "" {
 		limOpts.MaxID = opt.maxID
 	}
-	if opt.sinceID > 0 {
+	if opt.sinceID != "" {
 		limOpts.SinceID = opt.sinceID
 	}
 
--- a/cmd/lists.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/lists.go	Sat Feb 04 13:17:17 2023 +0100
@@ -15,8 +15,8 @@
 )
 
 var listsOpts struct {
-	listID     int64
-	accountID  int64
+	listID     madon.ActivityID
+	accountID  madon.ActivityID
 	accountIDs string
 	title      string
 
@@ -51,20 +51,20 @@
 	listsCmd.PersistentFlags().UintVarP(&listsOpts.keep, "keep", "k", 0, "Limit number of results")
 	listsCmd.PersistentFlags().BoolVar(&listsOpts.all, "all", false, "Fetch all results")
 
-	listsCmd.PersistentFlags().Int64VarP(&listsOpts.listID, "list-id", "G", 0, "List ID")
+	listsCmd.PersistentFlags().StringVarP(&listsOpts.listID, "list-id", "G", "", "List ID")
 
-	listsGetSubcommand.Flags().Int64VarP(&listsOpts.accountID, "account-id", "a", 0, "Account ID number")
+	listsGetSubcommand.Flags().StringVarP(&listsOpts.accountID, "account-id", "a", "", "Account ID number")
 	// XXX accountUID?
 
-	listsGetAccountsSubcommand.Flags().Int64VarP(&listsOpts.listID, "list-id", "G", 0, "List ID")
+	listsGetAccountsSubcommand.Flags().StringVarP(&listsOpts.listID, "list-id", "G", "", "List ID")
 
 	listsCreateSubcommand.Flags().StringVar(&listsOpts.title, "title", "", "List title")
 	listsUpdateSubcommand.Flags().StringVar(&listsOpts.title, "title", "", "List title")
 
 	listsAddAccountsSubcommand.Flags().StringVar(&listsOpts.accountIDs, "account-ids", "", "Comma-separated list of account IDs")
-	listsAddAccountsSubcommand.Flags().Int64VarP(&listsOpts.accountID, "account-id", "a", 0, "Account ID number")
+	listsAddAccountsSubcommand.Flags().StringVarP(&listsOpts.accountID, "account-id", "a", "", "Account ID number")
 	listsRemoveAccountsSubcommand.Flags().StringVar(&listsOpts.accountIDs, "account-ids", "", "Comma-separated list of account IDs")
-	listsRemoveAccountsSubcommand.Flags().Int64VarP(&listsOpts.accountID, "account-id", "a", 0, "Account ID number")
+	listsRemoveAccountsSubcommand.Flags().StringVarP(&listsOpts.accountID, "account-id", "a", "", "Account ID number")
 }
 
 var listsSubcommands = []*cobra.Command{
@@ -145,7 +145,7 @@
 	var obj interface{}
 	var err error
 
-	if opt.listID > 0 {
+	if opt.listID != "" {
 		var list *madon.List
 		list, err = gClient.GetList(opt.listID)
 		obj = list
@@ -178,7 +178,7 @@
 func listsGetAccountsRunE(cmd *cobra.Command, args []string) error {
 	opt := listsOpts
 
-	if opt.listID <= 0 {
+	if opt.listID == "" {
 		return errors.New("missing list ID")
 	}
 
@@ -237,12 +237,12 @@
 
 	switch cmd.Name() {
 	case "create":
-		if opt.listID > 0 {
+		if opt.listID != "" {
 			return errors.New("list ID should not be provided with create")
 		}
 		action = actionCreate
 	case "update":
-		if opt.listID <= 0 {
+		if opt.listID == "" {
 			return errors.New("list ID is required")
 		}
 		action = actionUpdate
@@ -300,19 +300,19 @@
 func listsAddRemoveAccountsRunE(cmd *cobra.Command, args []string) error {
 	opt := listsOpts
 
-	if opt.listID <= 0 {
+	if opt.listID == "" {
 		return errors.New("missing list ID")
 	}
 
-	var ids []int64
+	var ids []madon.ActivityID
 	var err error
 	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 opt.accountID != "" { // Allow --account-id
+		ids = []madon.ActivityID{opt.accountID}
 	}
 	if len(ids) < 1 {
 		return errors.New("missing account IDs")
--- a/cmd/madon.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/madon.go	Sat Feb 04 13:17:17 2023 +0100
@@ -6,7 +6,6 @@
 package cmd
 
 import (
-	"strconv"
 	"strings"
 
 	"github.com/McKael/madon/v3"
@@ -104,18 +103,16 @@
 }
 
 // splitIDs splits a list of IDs into an int64 array
-func splitIDs(ids string) (list []int64, err error) {
-	var i int64
+func splitIDs(ids string) (list []madon.ActivityID, err error) {
 	if ids == "" {
 		return
 	}
 	l := strings.Split(ids, ",")
 	for _, s := range l {
-		i, err = strconv.ParseInt(s, 10, 64)
-		if err != nil {
+		if s == "" {
 			return
 		}
-		list = append(list, i)
+		list = append(list, s)
 	}
 	return
 }
--- a/cmd/media.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/media.go	Sat Feb 04 13:17:17 2023 +0100
@@ -18,7 +18,7 @@
 var mediaFlags *flag.FlagSet
 
 var mediaOpts struct {
-	mediaID     int64
+	mediaID     madon.ActivityID
 	filePath    string
 	description string
 	focus       string
@@ -48,7 +48,7 @@
 	RootCmd.AddCommand(mediaCmd)
 
 	mediaCmd.Flags().StringVar(&mediaOpts.filePath, "file", "", "Path of the media file")
-	mediaCmd.Flags().Int64Var(&mediaOpts.mediaID, "update", 0, "Media to update (ID)")
+	mediaCmd.Flags().StringVar(&mediaOpts.mediaID, "update", "", "Media to update (ID)")
 
 	mediaCmd.Flags().StringVar(&mediaOpts.description, "description", "", "Plain text description")
 	mediaCmd.Flags().StringVar(&mediaOpts.focus, "focus", "", "Focal point")
@@ -61,10 +61,10 @@
 	opt := mediaOpts
 
 	if opt.filePath == "" {
-		if opt.mediaID < 1 {
+		if opt.mediaID == "" {
 			return errors.New("no media file name provided")
 		}
-	} else if opt.mediaID > 0 {
+	} else if opt.mediaID != "" {
 		return errors.New("cannot use both --file and --update")
 	}
 
@@ -102,13 +102,13 @@
 }
 
 // uploadFile uploads a media file and returns the attachment ID
-func uploadFile(filePath string) (int64, error) {
+func uploadFile(filePath string) (madon.ActivityID, error) {
 	attachment, err := gClient.UploadMedia(filePath, "", "")
 	if err != nil {
-		return 0, err
+		return "", err
 	}
 	if attachment == nil {
-		return 0, nil
+		return "", nil
 	}
 	return attachment.ID, nil
 }
--- a/cmd/notifications.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/notifications.go	Sat Feb 04 13:17:17 2023 +0100
@@ -17,7 +17,7 @@
 
 var notificationsOpts struct {
 	list, clear, dismiss bool
-	notifID              int64
+	notifID              madon.ActivityID
 	types                string
 	excludeTypes         string
 }
@@ -50,7 +50,7 @@
 	notificationsCmd.Flags().BoolVar(&notificationsOpts.list, "list", false, "List all current notifications")
 	notificationsCmd.Flags().BoolVar(&notificationsOpts.clear, "clear", false, "Clear all current notifications")
 	notificationsCmd.Flags().BoolVar(&notificationsOpts.dismiss, "dismiss", false, "Delete a notification")
-	notificationsCmd.Flags().Int64Var(&notificationsOpts.notifID, "notification-id", 0, "Get a notification")
+	notificationsCmd.Flags().StringVar(&notificationsOpts.notifID, "notification-id", "", "Get a notification")
 	notificationsCmd.Flags().StringVar(&notificationsOpts.types, "notification-types", "", "Filter notifications (mention, favourite, reblog, follow)")
 	notificationsCmd.Flags().StringVar(&notificationsOpts.excludeTypes, "exclude-types", "", "Exclude notifications types (mention, favourite, reblog, follow)")
 }
@@ -58,7 +58,7 @@
 func notificationRunE(cmd *cobra.Command, args []string) error {
 	opt := notificationsOpts
 
-	if !opt.list && !opt.clear && opt.notifID < 1 {
+	if !opt.list && !opt.clear && opt.notifID == "" {
 		return errors.New("missing parameters")
 	}
 
@@ -67,7 +67,7 @@
 	}
 
 	var limOpts *madon.LimitParams
-	if accountsOpts.all || accountsOpts.limit > 0 || accountsOpts.sinceID > 0 || accountsOpts.maxID > 0 {
+	if accountsOpts.all || accountsOpts.limit > 0 || accountsOpts.sinceID != "" || accountsOpts.maxID != "" {
 		limOpts = new(madon.LimitParams)
 		limOpts.All = accountsOpts.all
 	}
@@ -75,11 +75,11 @@
 	if accountsOpts.limit > 0 {
 		limOpts.Limit = int(accountsOpts.limit)
 	}
-	if accountsOpts.maxID > 0 {
-		limOpts.MaxID = int64(accountsOpts.maxID)
+	if accountsOpts.maxID != "" {
+		limOpts.MaxID = accountsOpts.maxID
 	}
-	if accountsOpts.sinceID > 0 {
-		limOpts.SinceID = int64(accountsOpts.sinceID)
+	if accountsOpts.sinceID != "" {
+		limOpts.SinceID = accountsOpts.sinceID
 	}
 
 	var filterMap *map[string]bool
@@ -123,7 +123,7 @@
 			notifications = notifications[:accountsOpts.keep]
 		}
 		obj = notifications
-	} else if opt.notifID > 0 {
+	} else if opt.notifID != "" {
 		if opt.dismiss {
 			err = gClient.DismissNotification(opt.notifID)
 		} else {
--- a/cmd/status.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/status.go	Sat Feb 04 13:17:17 2023 +0100
@@ -20,14 +20,14 @@
 var statusPostFlags *flag.FlagSet
 
 var statusOpts struct {
-	statusID int64
+	statusID madon.ActivityID
 	unset    bool // TODO remove eventually?
 
 	// The following fields are used for the post/toot command
 	visibility     string
 	sensitive      bool
 	spoiler        string
-	inReplyToID    int64
+	inReplyToID    madon.ActivityID
 	mediaIDs       string
 	mediaFilePath  string
 	textFilePath   string
@@ -39,6 +39,9 @@
 	limit, keep uint
 	//sinceID, maxID int64
 	all bool
+
+	// Used to indicate whether `in-reply-to` flag is present or not.
+	_hasReplyTo bool
 }
 
 func init() {
@@ -48,7 +51,7 @@
 	statusCmd.AddCommand(statusSubcommands...)
 
 	// Global flags
-	statusCmd.PersistentFlags().Int64VarP(&statusOpts.statusID, "status-id", "s", 0, "Status ID number")
+	statusCmd.PersistentFlags().StringVarP(&statusOpts.statusID, "status-id", "s", "", "Status ID number")
 	statusCmd.PersistentFlags().UintVarP(&statusOpts.limit, "limit", "l", 0, "Limit number of API results")
 	statusCmd.PersistentFlags().UintVarP(&statusOpts.keep, "keep", "k", 0, "Limit number of results")
 	//statusCmd.PersistentFlags().Int64Var(&statusOpts.sinceID, "since-id", 0, "Request IDs greater than a value")
@@ -65,7 +68,7 @@
 	statusPostSubcommand.Flags().StringVar(&statusOpts.mediaIDs, "media-ids", "", "Comma-separated list of media IDs")
 	statusPostSubcommand.Flags().StringVarP(&statusOpts.mediaFilePath, "file", "f", "", "Media file name")
 	statusPostSubcommand.Flags().StringVar(&statusOpts.textFilePath, "text-file", "", "Text file name (message content)")
-	statusPostSubcommand.Flags().Int64VarP(&statusOpts.inReplyToID, "in-reply-to", "r", 0, "Status ID to reply to")
+	statusPostSubcommand.Flags().StringVarP(&statusOpts.inReplyToID, "in-reply-to", "r", "", "Status ID to reply to")
 	statusPostSubcommand.Flags().BoolVar(&statusOpts.stdin, "stdin", false, "Read message content from standard input")
 	statusPostSubcommand.Flags().BoolVar(&statusOpts.addMentions, "add-mentions", false, "Add mentions when replying")
 	statusPostSubcommand.Flags().BoolVar(&statusOpts.sameVisibility, "same-visibility", false, "Use same visibility as original message (for replies)")
@@ -94,7 +97,7 @@
 	//Long:    `TBW...`, // TODO
 	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
 		// This is common to status and all status subcommands but "post"
-		if statusOpts.statusID < 1 && cmd.Name() != "post" {
+		if statusOpts.statusID == "" && cmd.Name() != "post" {
 			return errors.New("missing status ID")
 		}
 		return madonInit(true)
@@ -241,6 +244,8 @@
 The default visibility can be set in the configuration file with the option
 'default_visibility' (or with an environmnent variable).`,
 	RunE: func(cmd *cobra.Command, args []string) error {
+		// Update the extra flag to reflect if `in-reply-to` was present or not
+		statusOpts._hasReplyTo = cmd.Flags().Lookup("in-reply-to").Changed
 		return statusSubcommandRunE(cmd.Name(), args)
 	},
 }
--- a/cmd/suggestions.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/suggestions.go	Sat Feb 04 13:17:17 2023 +0100
@@ -15,7 +15,7 @@
 )
 
 var suggestionsOpts struct {
-	accountID  int64
+	accountID  madon.ActivityID
 	accountIDs string
 
 	//limit uint
@@ -41,7 +41,7 @@
 	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().StringVarP(&suggestionsOpts.accountID, "account-id", "a", "", "Account ID number")
 	suggestionsDeleteSubcommand.Flags().StringVar(&suggestionsOpts.accountIDs, "account-ids", "", "Comma-separated list of account IDs")
 }
 
@@ -116,13 +116,13 @@
 
 func suggestionsDeleteRunE(cmd *cobra.Command, args []string) error {
 	opt := suggestionsOpts
-	var ids []int64
+	var ids []madon.ActivityID
 	var err error
 
-	if opt.accountID < 1 && len(opt.accountIDs) == 0 {
+	if opt.accountID == "" && len(opt.accountIDs) == 0 {
 		return errors.New("missing account IDs")
 	}
-	if opt.accountID > 0 && len(opt.accountIDs) > 0 {
+	if opt.accountID != "" && len(opt.accountIDs) > 0 {
 		return errors.New("incompatible options")
 	}
 
@@ -130,8 +130,8 @@
 	if err != nil {
 		return errors.New("cannot parse account IDs")
 	}
-	if opt.accountID > 0 { // Allow --account-id
-		ids = []int64{opt.accountID}
+	if opt.accountID != "" { // Allow --account-id
+		ids = []madon.ActivityID{opt.accountID}
 	}
 	if len(ids) < 1 {
 		return errors.New("missing account IDs")
--- a/cmd/timelines.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/timelines.go	Sat Feb 04 13:17:17 2023 +0100
@@ -17,7 +17,7 @@
 var timelineOpts struct {
 	local, onlyMedia bool
 	limit, keep      uint
-	sinceID, maxID   int64
+	sinceID, maxID   madon.ActivityID
 }
 
 // timelineCmd represents the timelines command
@@ -47,25 +47,25 @@
 	timelineCmd.Flags().BoolVar(&timelineOpts.onlyMedia, "only-media", false, "Only statuses with media attachments")
 	timelineCmd.Flags().UintVarP(&timelineOpts.limit, "limit", "l", 0, "Limit number of API results")
 	timelineCmd.Flags().UintVarP(&timelineOpts.keep, "keep", "k", 0, "Limit number of results")
-	timelineCmd.PersistentFlags().Int64Var(&timelineOpts.sinceID, "since-id", 0, "Request IDs greater than a value")
-	timelineCmd.PersistentFlags().Int64Var(&timelineOpts.maxID, "max-id", 0, "Request IDs less (or equal) than a value")
+	timelineCmd.PersistentFlags().StringVar(&timelineOpts.sinceID, "since-id", "", "Request IDs greater than a value")
+	timelineCmd.PersistentFlags().StringVar(&timelineOpts.maxID, "max-id", "", "Request IDs less (or equal) than a value")
 }
 
 func timelineRunE(cmd *cobra.Command, args []string) error {
 	opt := timelineOpts
 	var limOpts *madon.LimitParams
 
-	if opt.limit > 0 || opt.sinceID > 0 || opt.maxID > 0 {
+	if opt.limit > 0 || opt.sinceID != "" || opt.maxID != "" {
 		limOpts = new(madon.LimitParams)
 	}
 
 	if opt.limit > 0 {
 		limOpts.Limit = int(opt.limit)
 	}
-	if opt.maxID > 0 {
+	if opt.maxID != "" {
 		limOpts.MaxID = opt.maxID
 	}
-	if opt.sinceID > 0 {
+	if opt.sinceID != "" {
 		limOpts.SinceID = opt.sinceID
 	}
 
--- a/cmd/toot.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/cmd/toot.go	Sat Feb 04 13:17:17 2023 +0100
@@ -29,7 +29,7 @@
 	tootAliasCmd.Flags().StringVar(&statusOpts.mediaIDs, "media-ids", "", "Comma-separated list of media IDs")
 	tootAliasCmd.Flags().StringVarP(&statusOpts.mediaFilePath, "file", "f", "", "Media attachment file name")
 	tootAliasCmd.Flags().StringVar(&statusOpts.textFilePath, "text-file", "", "Text file name (message content)")
-	tootAliasCmd.Flags().Int64VarP(&statusOpts.inReplyToID, "in-reply-to", "r", 0, "Status ID to reply to")
+	tootAliasCmd.Flags().StringVarP(&statusOpts.inReplyToID, "in-reply-to", "r", "", "Status ID to reply to")
 	tootAliasCmd.Flags().BoolVar(&statusOpts.stdin, "stdin", false, "Read message content from standard input")
 	tootAliasCmd.Flags().BoolVar(&statusOpts.addMentions, "add-mentions", false, "Add mentions when replying")
 	tootAliasCmd.Flags().BoolVar(&statusOpts.sameVisibility, "same-visibility", false, "Use same visibility as original message (for replies)")
@@ -64,6 +64,8 @@
 		if err := madonInit(true); err != nil {
 			return err
 		}
+		// Update the extra flag to reflect if `in-reply-to` was present or not
+		statusOpts._hasReplyTo = cmd.Flags().Lookup("in-reply-to").Changed
 		return statusSubcommandRunE("post", args)
 	},
 }
@@ -85,7 +87,12 @@
 		return nil, errors.Errorf("invalid visibility argument value '%s'", opt.visibility)
 	}
 
-	if opt.inReplyToID < 0 {
+	// Bit of a fudge but there's no easy way to tell if a string flag
+	// is empty by default or empty by assignment.  Can't use a pointer
+	// and have `nil` be "unset" because Cobra will crash if we send it
+	// a `nil` as the recepient for a flag variable.  Hence using an
+	// extra struct member as a flag to indicate set/unset.
+	if opt._hasReplyTo && opt.inReplyToID == "" {
 		return nil, errors.New("invalid in-reply-to argument value")
 	}
 
@@ -98,7 +105,7 @@
 		return nil, errors.New("toot is empty")
 	}
 
-	if opt.inReplyToID > 0 {
+	if opt.inReplyToID != "" {
 		var initialStatus *madon.Status
 		var preserveVis bool
 		if opt.sameVisibility &&
@@ -143,7 +150,7 @@
 		if err != nil {
 			return nil, errors.Wrap(err, "cannot attach media file")
 		}
-		if fileMediaID > 0 {
+		if fileMediaID != "" {
 			ids = append(ids, fileMediaID)
 		}
 	}
--- a/printer/plainprinter.go	Sat Feb 04 11:00:55 2023 +0000
+++ b/printer/plainprinter.go	Sat Feb 04 13:17:17 2023 +0100
@@ -175,7 +175,7 @@
 }
 
 func (p *PlainPrinter) plainPrintAccount(a *madon.Account, w io.Writer, indent string) error {
-	indentedPrint(w, indent, true, false, "Account ID", "%d (%s)", a.ID, a.Username)
+	indentedPrint(w, indent, true, false, "Account ID", "%s (%s)", a.ID, a.Username)
 	indentedPrint(w, indent, false, false, "User ID", "%s", a.Acct)
 	indentedPrint(w, indent, false, false, "Display name", "%s", a.DisplayName)
 	indentedPrint(w, indent, false, false, "Creation date", "%v", a.CreatedAt.Local())
@@ -192,7 +192,7 @@
 	indentedPrint(w, indent, false, true, "User note", "%s", html2string(a.Note)) // XXX too long?
 	if a.Moved != nil {
 		m := a.Moved
-		indentedPrint(w, indent+p.Indent, true, false, "Moved to account ID", "%d (%s)", m.ID, m.Username)
+		indentedPrint(w, indent+p.Indent, true, false, "Moved to account ID", "%s (%s)", m.ID, m.Username)
 		indentedPrint(w, indent+p.Indent, false, false, "New user ID", "%s", m.Acct)
 		indentedPrint(w, indent+p.Indent, false, false, "New display name", "%s", m.DisplayName)
 	}
@@ -218,7 +218,7 @@
 }
 
 func (p *PlainPrinter) plainPrintAttachment(a *madon.Attachment, w io.Writer, indent string) error {
-	indentedPrint(w, indent, true, false, "Attachment ID", "%d", a.ID)
+	indentedPrint(w, indent, true, false, "Attachment ID", "%s", a.ID)
 	indentedPrint(w, indent, false, false, "Type", "%s", a.Type)
 	indentedPrint(w, indent, false, false, "Local URL", "%s", a.URL)
 	if a.RemoteURL != nil {
@@ -269,7 +269,7 @@
 	indentedPrint(w, indent, false, true, "Version", "%s", i.Version)
 	if i.ContactAccount != nil {
 		c := i.ContactAccount
-		indentedPrint(w, indent+p.Indent, true, false, "Contact account ID", "%d (%s)", c.ID, c.Username)
+		indentedPrint(w, indent+p.Indent, true, false, "Contact account ID", "%s (%s)", c.ID, c.Username)
 		indentedPrint(w, indent+p.Indent, false, false, "Contact user ID", "%s", c.Acct)
 		indentedPrint(w, indent+p.Indent, false, false, "Contact display name", "%s", c.DisplayName)
 	}
@@ -282,17 +282,17 @@
 }
 
 func (p *PlainPrinter) plainPrintList(l *madon.List, w io.Writer, indent string) error {
-	indentedPrint(w, indent, true, false, "List ID", "%d", l.ID)
+	indentedPrint(w, indent, true, false, "List ID", "%s", l.ID)
 	indentedPrint(w, indent, false, false, "Title", "%s", l.Title)
 	return nil
 }
 
 func (p *PlainPrinter) plainPrintNotification(n *madon.Notification, w io.Writer, indent string) error {
-	indentedPrint(w, indent, true, false, "Notification ID", "%d", n.ID)
+	indentedPrint(w, indent, true, false, "Notification ID", "%s", n.ID)
 	indentedPrint(w, indent, false, false, "Type", "%s", n.Type)
 	indentedPrint(w, indent, false, false, "Timestamp", "%v", n.CreatedAt.Local())
 	if n.Account != nil {
-		indentedPrint(w, indent+p.Indent, true, false, "Account", "(%d) @%s - %s",
+		indentedPrint(w, indent+p.Indent, true, false, "Account", "(%s) @%s - %s",
 			n.Account.ID, n.Account.Acct, n.Account.DisplayName)
 	}
 	if n.Status != nil {
@@ -302,7 +302,7 @@
 }
 
 func (p *PlainPrinter) plainPrintRelationship(r *madon.Relationship, w io.Writer, indent string) error {
-	indentedPrint(w, indent, true, false, "Account ID", "%d", r.ID)
+	indentedPrint(w, indent, true, false, "Account ID", "%s", r.ID)
 	indentedPrint(w, indent, false, false, "Following", "%v", r.Following)
 	//indentedPrint(w, indent, false, false, "Showing reblogs", "%v", r.ShowingReblogs)
 	indentedPrint(w, indent, false, false, "Followed-by", "%v", r.FollowedBy)
@@ -315,7 +315,7 @@
 }
 
 func (p *PlainPrinter) plainPrintReport(r *madon.Report, w io.Writer, indent string) error {
-	indentedPrint(w, indent, true, false, "Report ID", "%d", r.ID)
+	indentedPrint(w, indent, true, false, "Report ID", "%s", r.ID)
 	indentedPrint(w, indent, false, false, "Action taken", "%s", r.ActionTaken)
 	return nil
 }
@@ -342,7 +342,7 @@
 }
 
 func (p *PlainPrinter) plainPrintStatus(s *madon.Status, w io.Writer, indent string) error {
-	indentedPrint(w, indent, true, false, "Status ID", "%d", s.ID)
+	indentedPrint(w, indent, true, false, "Status ID", "%s", s.ID)
 	if s.Account != nil {
 		author := s.Account.Acct
 		if s.Account.DisplayName != "" {
@@ -370,8 +370,8 @@
 	}
 
 	indentedPrint(w, indent, false, false, "Contents", "%s", html2string(s.Content))
-	if s.InReplyToID != nil && *s.InReplyToID > 0 {
-		indentedPrint(w, indent, false, false, "In-Reply-To", "%d", *s.InReplyToID)
+	if s.InReplyToID != nil && *s.InReplyToID != "" {
+		indentedPrint(w, indent, false, false, "In-Reply-To", "%s", *s.InReplyToID)
 	}
 	if s.Reblogged {
 		indentedPrint(w, indent, false, false, "Reblogged", "%v", s.Reblogged)
@@ -380,7 +380,7 @@
 	// Display minimum details of attachments
 	//return p.PrintObj(s.MediaAttachments, w, indent+p.Indent)
 	for _, a := range s.MediaAttachments {
-		indentedPrint(w, indent+p.Indent, true, false, "Attachment ID", "%d", a.ID)
+		indentedPrint(w, indent+p.Indent, true, false, "Attachment ID", "%s", a.ID)
 		if a.TextURL != nil && *a.TextURL != "" {
 			indentedPrint(w, indent+p.Indent, true, false, "Text URL", "%s", *a.TextURL)
 		} else if a.URL != "" {