Compare commits

...

41 Commits

Author SHA1 Message Date
dependabot[bot] 43a1766244 Bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-16 11:04:20 +01:00
dependabot[bot] cdfcf95b82 Bump github.com/jedib0t/go-pretty/v6 from 6.4.3 to 6.4.4
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.4.3 to 6.4.4.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.4.3...v6.4.4)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-01 01:05:51 +01:00
dependabot[bot] 729dad63cf Bump github.com/microcosm-cc/bluemonday from 1.0.21 to 1.0.22
Bumps [github.com/microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) from 1.0.21 to 1.0.22.
- [Release notes](https://github.com/microcosm-cc/bluemonday/releases)
- [Commits](https://github.com/microcosm-cc/bluemonday/compare/v1.0.21...v1.0.22)

---
updated-dependencies:
- dependency-name: github.com/microcosm-cc/bluemonday
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-01 00:44:22 +01:00
dependabot[bot] 81709a867b Bump github.com/mattn/go-mastodon from 0.0.5 to 0.0.6
Bumps [github.com/mattn/go-mastodon](https://github.com/mattn/go-mastodon) from 0.0.5 to 0.0.6.
- [Release notes](https://github.com/mattn/go-mastodon/releases)
- [Commits](https://github.com/mattn/go-mastodon/compare/v0.0.5...v0.0.6)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-mastodon
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 05:12:37 +01:00
dependabot[bot] b16bc98fd5 Bump github.com/jedib0t/go-pretty/v6 from 6.4.2 to 6.4.3
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.4.2 to 6.4.3.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.4.2...v6.4.3)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-01 03:26:26 +01:00
Christian Muehlhaeuser 8de3c05a58
chore: bump go-mastodon dep 2022-11-15 19:08:37 +01:00
Christian Muehlhaeuser 6a4ea32e42 fix: linter errors 2022-11-15 18:38:02 +01:00
Christian Muehlhaeuser 4d6c05aea4 ci: add linters 2022-11-15 18:38:02 +01:00
dependabot[bot] a48263cac4 Bump github.com/spf13/cobra from 1.0.0 to 1.6.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.0.0 to 1.6.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.0.0...v1.6.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-15 18:37:32 +01:00
dependabot[bot] 8e1fc223a8 Bump github.com/jedib0t/go-pretty/v6 from 6.0.5 to 6.4.2
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.0.5 to 6.4.2.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.0.5...v6.4.2)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-15 18:30:15 +01:00
dependabot[bot] 50778bff1b Bump github.com/microcosm-cc/bluemonday from 1.0.4 to 1.0.21
Bumps [github.com/microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) from 1.0.4 to 1.0.21.
- [Release notes](https://github.com/microcosm-cc/bluemonday/releases)
- [Commits](https://github.com/microcosm-cc/bluemonday/compare/v1.0.4...v1.0.21)

---
updated-dependencies:
- dependency-name: github.com/microcosm-cc/bluemonday
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-15 17:14:34 +01:00
dependabot[bot] ff25e9ed96 Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-15 17:14:15 +01:00
dependabot[bot] ead1d87108 Bump actions/setup-go from 2 to 3
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-15 17:13:58 +01:00
Christian Muehlhaeuser df99490a1e chore: add dependabot config 2022-11-15 17:08:25 +01:00
Christian Muehlhaeuser c21c4a78c4
Fix goreportcard badge 2021-05-19 06:46:50 +02:00
Christian Muehlhaeuser 8404cea804
Disable CGO for goreleaser builds 2020-11-07 06:14:16 +01:00
Christian Muehlhaeuser 562038db56
Update GitHub workflow 2020-11-07 06:11:57 +01:00
Christian Muehlhaeuser 4a70999afb
Add links to more builds to README 2020-10-06 04:15:37 +02:00
Christian Muehlhaeuser 38812fa213
Add OpenBSD build to goreleaser config 2020-10-06 04:14:55 +02:00
Christian Muehlhaeuser e57cb0710e
Parse flags before accessing config
Fixes #13.
2020-10-06 04:07:43 +02:00
Christian Muehlhaeuser d15fedd774
Update goreleaser config 2020-10-04 10:26:47 +02:00
Christian Muehlhaeuser 7af7c82152
Test against Go 1.13+ 2020-10-04 03:50:44 +02:00
Christian Muehlhaeuser 8f36633740
Bump dependencies to latest stable releases 2020-10-04 03:27:40 +02:00
Christian Muehlhaeuser aacba34ab3
Bump go-pretty to v6.0.5 2020-10-04 03:22:39 +02:00
Christian Muehlhaeuser 5b70cc0c1a
Automatically prefix 'https' if the protocol is missing from the URL 2020-10-04 03:19:10 +02:00
Christian Muehlhaeuser 3f563e17e6
Show helper text for empty tables 2020-08-23 12:10:03 +02:00
Christian Muehlhaeuser b43f0d52a9
Make progressBarWidth a const 2020-08-23 12:09:54 +02:00
Christian Muehlhaeuser 339bdd1c85
End comment with period 2020-08-23 12:09:38 +02:00
Christian Muehlhaeuser 77b97800fd
Remove obsolete Config.Set return value 2020-08-23 12:09:26 +02:00
Christian Muehlhaeuser 9dfd746a5a
Enable builds for Windows, BSD, and homebrew
Fixes #8.
2020-08-23 10:35:28 +02:00
Christian Muehlhaeuser 2822457d13
Move badges to top of README 2020-08-23 07:05:04 +02:00
Christian Muehlhaeuser 01113437a3
Remove TravisCI config 2020-08-23 07:01:59 +02:00
Christian Muehlhaeuser 9db7002e1f
Add GitHub metadata & workflow 2020-08-23 07:00:42 +02:00
Christian Muehlhaeuser b727b5f719
Add an extra newline before search results 2020-08-23 06:58:54 +02:00
Christian Muehlhaeuser 4414c515df
Switch to go-pretty's table writer 2020-08-23 06:42:32 +02:00
Christian Muehlhaeuser 9c84b0037a
Fix deprecated goreleaser config syntax 2020-08-22 15:15:02 +02:00
Christian Muehlhaeuser 763bd6ce3f
go mod tidy 2020-08-22 15:12:37 +02:00
Erwin Ernst Steinhammer a14d1e4934 Update README.md
Changed the example. First of all, now it fits better to the example output, second human subjects who try this tool out won't run in the problem that there account is freezed for several hours for there first test. And changed the example output to mach better with the real output.
2020-01-16 01:52:06 +01:00
Christian Muehlhaeuser ddcf0d9696
Drop support for Go 1.8 2019-09-18 02:30:55 +02:00
Christian Muehlhaeuser ad105e3711 Test against Go 1.13 on TravisCI 2019-09-18 02:05:16 +02:00
tachiniererin f92cefd853 Add time and URL to search output (#6) 2019-08-14 21:03:31 +02:00
16 changed files with 383 additions and 159 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: muesli

14
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
labels:
- "dependencies"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
labels:
- "dependencies"

29
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: build
on: [push, pull_request]
jobs:
build:
strategy:
matrix:
go-version: [~1.13, ^1]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
env:
GO111MODULE: "on"
steps:
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v3
- name: Download Go modules
run: go mod download
- name: Build
run: go build -v ./...
- name: Test
run: go test ./...

28
.github/workflows/lint-soft.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: lint-soft
on:
push:
pull_request:
permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
pull-requests: read
jobs:
golangci:
name: lint-soft
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: ^1
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Optional: golangci-lint command line arguments.
args: --config .golangci-soft.yml --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
only-new-issues: true

28
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: lint
on:
push:
pull_request:
permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
pull-requests: read
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: ^1
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Optional: golangci-lint command line arguments.
#args:
# Optional: show only new issues if it's a pull request. The default value is `false`.
only-new-issues: true

47
.golangci-soft.yml Normal file
View File

@ -0,0 +1,47 @@
run:
tests: false
issues:
include:
- EXC0001
- EXC0005
- EXC0011
- EXC0012
- EXC0013
max-issues-per-linter: 0
max-same-issues: 0
linters:
enable:
# - dupl
- exhaustive
# - exhaustivestruct
- goconst
- godot
- godox
- gomnd
- gomoddirectives
- goprintffuncname
- ifshort
# - lll
- misspell
- nakedret
- nestif
- noctx
- nolintlint
- prealloc
- wrapcheck
# disable default linters, they are already enabled in .golangci.yml
disable:
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck

29
.golangci.yml Normal file
View File

@ -0,0 +1,29 @@
run:
tests: false
issues:
include:
- EXC0001
- EXC0005
- EXC0011
- EXC0012
- EXC0013
max-issues-per-linter: 0
max-same-issues: 0
linters:
enable:
- bodyclose
- exportloopref
- goimports
- gosec
- nilerr
- predeclared
- revive
- rowserrcheck
- sqlclosecheck
- tparallel
- unconvert
- unparam
- whitespace

View File

@ -2,14 +2,19 @@ env:
- GO111MODULE=on
before:
hooks:
- go mod download
- go mod download
builds:
-
- id: "mastotool"
env:
- CGO_ENABLED=0
binary: mastotool
ldflags: -s -w -X main.Version={{ .Version }} -X main.CommitSHA={{ .Commit }}
goos:
- linux
- freebsd
- openbsd
- darwin
- windows
goarch:
- amd64
- arm64
@ -18,29 +23,55 @@ builds:
goarm:
- 6
- 7
ignore:
- goos: darwin
goarch: 386
- goos: linux
goarch: arm
goarm: 7
sign:
artifacts: checksum
archives:
-
- id: default
builds:
- mastotool
format_overrides:
- goos: windows
format: zip
replacements:
darwin: Darwin
linux: Linux
windows: Windows
darwin: Darwin
386: i386
amd64: x86_64
nfpms:
- builds:
- mastotool
vendor: muesli
homepage: "https://fribbledom.com/"
maintainer: "Christian Muehlhaeuser <muesli@gmail.com>"
description: "Mastodon CLI tool & statistics generator"
license: MIT
formats:
- deb
- rpm
bindir: /usr/bin
brews:
- goarm: 6
tap:
owner: muesli
name: homebrew-tap
commit_author:
name: "Christian Muehlhaeuser"
email: "muesli@gmail.com"
homepage: "https://fribbledom.com/"
description: "Mastodon CLI tool & statistics generator"
# skip_upload: true
signs:
- artifacts: checksum
checksum:
name_template: 'checksums.txt'
name_template: "checksums.txt"
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- "^docs:"
- "^test:"

View File

@ -1,26 +0,0 @@
language: go
os:
- linux
- osx
go:
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
- tip
matrix:
allow_failures:
- go: tip
env:
global:
GO111MODULE=on
notifications:
email:
on_success: change
on_failure: always

View File

@ -1,19 +1,31 @@
mastotool
=========
mastotool offers a collection of tools to work with your Mastodon account
[![Latest Release](https://img.shields.io/github/release/muesli/mastotool.svg)](https://github.com/muesli/mastotool/releases)
[![Build Status](https://github.com/muesli/mastotool/workflows/build/badge.svg)](https://github.com/muesli/mastotool/actions)
[![Go ReportCard](https://goreportcard.com/badge/muesli/mastotool)](https://goreportcard.com/report/muesli/mastotool)
[![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](https://pkg.go.dev/github.com/muesli/mastotool)
mastotool offers a collection of command-line tools to work with your Mastodon account
## Installation
### Packages & Binaries
### Packages
#### Linux
- Arch Linux: [mastotool](https://aur.archlinux.org/packages/mastotool/)
- [Binaries](https://github.com/muesli/mastotool/releases) for Linux, macOS & Windows
- [Packages](https://github.com/muesli/mastotool/releases) in Debian & RPM formats
#### macOS
- macOS with [Homebrew](https://brew.sh/): `brew tap muesli/tap && brew install mastotool`
### Binaries
- [Binaries](https://github.com/muesli/mastotool/releases) for Linux, FreeBSD, OpenBSD, macOS & Windows
### From source
Make sure you have a working Go environment (Go 1.8 or higher is required).
See the [install instructions](http://golang.org/doc/install.html).
Make sure you have a working Go environment (Go 1.9 or higher is required).
See the [install instructions](https://golang.org/doc/install.html).
Compiling mastotool is easy, simply run:
@ -58,12 +70,12 @@ mastotool search <string>
## Example
```
$ mastotool stats
$ mastotool stats -r 100
Which instance to connect to: https://mastodon.social
Username (email): some_user@domain.tld
Password: ********
Loading toots for some_user 100 of 100 [#>---------------------------] 100.00%
Loading toots for some_user 100 of 100 [############################>] 100.00%
Total toots: 100 (excluding replies & boosts)
Toots per day: 1.00 (account created 100 days ago)
@ -119,9 +131,3 @@ Tags used that got the most boosts Boos
Some tag 5
Another tag 1
```
## Development
[![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](https://godoc.org/github.com/muesli/mastotool)
[![Build Status](https://travis-ci.org/muesli/mastotool.svg?branch=master)](https://travis-ci.org/muesli/mastotool)
[![Go ReportCard](http://goreportcard.com/badge/muesli/mastotool)](http://goreportcard.com/report/muesli/mastotool)

View File

@ -5,15 +5,18 @@ import (
"io/ioutil"
)
// Option is a single configuration option.
type Option struct {
Name string
Value interface{}
}
// Config is a configuration file.
type Config struct {
Options []Option
}
// LoadConfig loads a configuration file.
func LoadConfig(filename string) (Config, error) {
config := Config{}
@ -26,14 +29,16 @@ func LoadConfig(filename string) (Config, error) {
return config, err
}
// Save saves the configuration to a file.
func (c Config) Save(filename string) error {
j, err := json.MarshalIndent(c, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(filename, j, 0644)
return ioutil.WriteFile(filename, j, 0600)
}
// Value returns the value of a configuration option.
func (c Config) Value(name string) interface{} {
for _, v := range c.Options {
if v.Name == name {
@ -44,7 +49,8 @@ func (c Config) Value(name string) interface{} {
return nil
}
func (c *Config) Set(name, value string) interface{} {
// Set sets the value of a configuration option.
func (c *Config) Set(name, value string) {
found := false
var opts []Option
for _, v := range c.Options {
@ -61,5 +67,4 @@ func (c *Config) Set(name, value string) interface{} {
}
c.Options = opts
return nil
}

14
go.mod
View File

@ -1,9 +1,13 @@
module github.com/muesli/mastotool
go 1.15
require (
github.com/mattn/go-mastodon v0.0.5-0.20190808074743-8a48862adc29
github.com/microcosm-cc/bluemonday v1.0.1
github.com/muesli/goprogressbar v0.0.0-20180221033428-8ba388894ed9
github.com/muesli/gotable v0.0.0-20180521192606-513fc87bb47c
github.com/spf13/cobra v0.0.5
github.com/jedib0t/go-pretty/v6 v6.4.4
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-mastodon v0.0.6
github.com/microcosm-cc/bluemonday v1.0.22
github.com/muesli/goprogressbar v0.2.0
github.com/spf13/cobra v1.6.1
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)

118
go.sum
View File

@ -1,69 +1,65 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-mastodon v0.0.3-0.20181122123740-c5945152ec75 h1:XFRFY3rgSleciUFYghNPE51HAdXK0XcrQPLvIAwZgRM=
github.com/mattn/go-mastodon v0.0.3-0.20181122123740-c5945152ec75/go.mod h1:/OSOSDJyV0OUlBuDV0Qrllizt3BJNj4Ir5xhckYRVmg=
github.com/mattn/go-mastodon v0.0.5-0.20190808074743-8a48862adc29 h1:p3d+nZHO9GDhvUBM1hEdr65dvI2L6cNngwuZ/YY55KM=
github.com/mattn/go-mastodon v0.0.5-0.20190808074743-8a48862adc29/go.mod h1:ZBkemyyYYhNAN5JJ0H/ZSW8HfPCW45rHFHyWNwSfpTA=
github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/muesli/goprogressbar v0.0.0-20180221033428-8ba388894ed9 h1:UD88B1EJwL8Lade/0iOy9YGaRb9PkGFse9bqzZb4odg=
github.com/muesli/goprogressbar v0.0.0-20180221033428-8ba388894ed9/go.mod h1:F7QqN7y15ODymk1BQifvSMHCqbHeBJH73/Nru18Ns7c=
github.com/muesli/gotable v0.0.0-20180521192606-513fc87bb47c h1:FenLlkB2dgPh+14ME2KGwFjkJdcUc9WhqyAILCwRUdA=
github.com/muesli/gotable v0.0.0-20180521192606-513fc87bb47c/go.mod h1:WPZDVvUBllqwXeaXhSmOSQiqjxBT5MzDBmW3qyOC09s=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jedib0t/go-pretty/v6 v6.4.4 h1:N+gz6UngBPF4M288kiMURPHELDMIhF/Em35aYuKrsSc=
github.com/jedib0t/go-pretty/v6 v6.4.4/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-mastodon v0.0.6 h1:lqU1sOeeIapaDsDUL6udDZIzMb2Wqapo347VZlaOzf0=
github.com/mattn/go-mastodon v0.0.6/go.mod h1:cg7RFk2pcUfHZw/IvKe1FUzmlq5KnLFqs7eV2PHplV8=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/microcosm-cc/bluemonday v1.0.22 h1:p2tT7RNzRdCi0qmwxG+HbqD6ILkmwter1ZwVZn1oTxA=
github.com/microcosm-cc/bluemonday v1.0.22/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
github.com/muesli/goprogressbar v0.2.0 h1:qsW8FigQF3n3YxDTQCihy2xRya8/JqlPHJi01Lbx5HA=
github.com/muesli/goprogressbar v0.2.0/go.mod h1:19yRWZtJozyS7m+fyTUK0rE76LABdnU7zp0BuyeDwLc=
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM=
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 h1:kkXA53yGe04D0adEYJwEVQjeBppL01Exg+fnMjfUraU=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak=
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b h1:MQE+LT/ABUuuvEZ+YQAMSXindAdUh7slEmAkup74op4=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

38
main.go
View File

@ -5,6 +5,7 @@ import (
"context"
"fmt"
"os"
"strings"
mastodon "github.com/mattn/go-mastodon"
"github.com/spf13/cobra"
@ -15,15 +16,32 @@ var (
self *mastodon.Account
configFile string
// RootCmd is the core command used for cli-arg parsing
// RootCmd is the core command used for cli-arg parsing.
RootCmd = &cobra.Command{
Use: "mastotool",
Short: "mastotool offers a collection of tools to work with your Mastodon account",
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := initClient(); err != nil {
return err
}
var err error
self, err = client.GetAccountCurrentUser(context.Background())
if err != nil {
return fmt.Errorf("Can't retrieve user: %s", err)
}
return nil
},
}
)
const (
progressBarWidth = 40
)
func registerApp(config *Config) (string, error) {
app, err := mastodon.RegisterApp(context.Background(), &mastodon.AppConfig{
Server: config.Value("instance").(string),
@ -61,7 +79,12 @@ func initClient() error {
if scanner.Err() != nil {
return fmt.Errorf("Can't open input: %s", err)
}
instance = scanner.Text()
if !strings.HasPrefix(instance, "http") {
instance = "https://" + instance
}
config.Set("instance", instance)
}
@ -110,19 +133,8 @@ func initClient() error {
func main() {
RootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "mastodon.json", "uses the specified config file")
if err := initClient(); err != nil {
fmt.Println(err)
os.Exit(1)
}
var err error
self, err = client.GetAccountCurrentUser(context.Background())
if err != nil {
fmt.Printf("Can't retrieve user: %s\n", err)
os.Exit(1)
}
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(-1)
os.Exit(1)
}
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"math"
"strings"
"time"
mastodon "github.com/mattn/go-mastodon"
"github.com/muesli/goprogressbar"
@ -32,7 +33,7 @@ func search(token string) error {
return fmt.Sprintf("%d of %d", p.Current, int64(math.Max(float64(p.Current), float64(self.StatusesCount))))
},
Current: 0,
Width: 40,
Width: progressBarWidth,
}
var pg mastodon.Pagination
@ -48,11 +49,13 @@ func search(token string) error {
abort := false
for _, s := range statuses {
if strings.Contains(strings.ToLower(cleanupContent(s.Content)), token) {
fmt.Println("\nFound toot:", cleanupContent(s.Content))
fmt.Println("\n\nFound toot:", cleanupContent(s.Content))
fmt.Println("Date:", s.CreatedAt.Format(time.RFC822))
fmt.Println("URL:", s.URL)
fmt.Println()
}
pb.Current += 1
pb.Current++
pb.LazyPrint()
}

View File

@ -5,15 +5,15 @@ import (
"fmt"
"html"
"math"
"os"
"sort"
"strconv"
"strings"
"time"
"github.com/jedib0t/go-pretty/v6/table"
mastodon "github.com/mattn/go-mastodon"
"github.com/microcosm-cc/bluemonday"
"github.com/muesli/goprogressbar"
"github.com/muesli/gotable"
"github.com/spf13/cobra"
)
@ -33,6 +33,7 @@ var (
stripper = bluemonday.StrictPolicy()
)
// Sort options.
const (
SortByLikes = iota
SortByBoosts
@ -77,7 +78,7 @@ func gatherStats() error {
return fmt.Sprintf("%d of %d", p.Current, int64(math.Max(float64(p.Current), float64(self.StatusesCount))))
},
Current: 0,
Width: 40,
Width: progressBarWidth,
}
var pg mastodon.Pagination
@ -97,7 +98,7 @@ func gatherStats() error {
return fmt.Errorf("Can't parse toot: %s", err)
}
pb.Current += 1
pb.Current++
pb.LazyPrint()
if maxToots > 0 && len(stats.Toots) >= maxToots {
@ -205,22 +206,22 @@ func parseToot(status *mastodon.Status, stats *stats) error {
return nil
}
type StatSorter struct {
type statSorter struct {
SortKey int
Key []string
Stats []*tootStat
}
func (a StatSorter) Len() int {
func (a statSorter) Len() int {
return len(a.Stats)
}
func (a StatSorter) Swap(i, j int) {
func (a statSorter) Swap(i, j int) {
a.Key[i], a.Key[j] = a.Key[j], a.Key[i]
a.Stats[i], a.Stats[j] = a.Stats[j], a.Stats[i]
}
func (a StatSorter) Less(i, j int) bool {
func (a statSorter) Less(i, j int) bool {
switch a.SortKey {
case SortByReplies:
return a.Stats[i].Replies < a.Stats[j].Replies
@ -246,28 +247,44 @@ func printTable(cols []string, emptyText string, data []kv) {
return data[i].Value > data[j].Value
})
col1 := columns - len(cols[1])
col2 := len(cols[1])
tab := gotable.NewTable(cols,
[]int64{-int64(col1), int64(col2)},
emptyText)
col1 := columns - len(cols[1]) - 8
// col2 := len(cols[1])
tab := table.NewWriter()
tab.SetOutputMirror(os.Stdout)
tab.SetStyle(table.StyleLight)
tab.SetAllowedRowLength(columns)
tab.AppendHeader(table.Row{cols[0], cols[1]})
tab.SetColumnConfigs([]table.ColumnConfig{
{Name: cols[0], WidthMax: col1},
{Name: cols[1]},
})
for i, kv := range data {
if i >= topN {
break
}
if len(kv.Key) > col1-4 {
kv.Key = kv.Key[:col1-4] + "..."
}
/*
if runewidth.StringWidth(kv.Key) > col1-4 {
kv.Key = kv.Key[:col1-4] + "..."
}
*/
tab.AppendRow([]interface{}{kv.Key, strconv.FormatInt(int64(kv.Value), 10)})
tab.AppendRow([]interface{}{kv.Key, kv.Value})
tab.AppendSeparator()
}
tab.Print()
if len(data) == 0 {
tab.AppendRow(table.Row{emptyText})
}
tab.Render()
fmt.Println()
}
//nolint:unparam
func printTootTable(cols []string, emptyText string, toots []string, tootStats []*tootStat, sortKey int) {
sort.Sort(sort.Reverse(StatSorter{sortKey, toots, tootStats}))
sort.Sort(sort.Reverse(statSorter{sortKey, toots, tootStats}))
var ss []kv
for k, v := range toots {