* Delete follows/followers in bulk
* by account age
* by age of last post
+
+## Usage
+
+```
+dead-tooter login --host hackers.town
+```
--- /dev/null
+package tooter
+
+import "github.com/spf13/cobra"
+
+func init() {
+ rootCmd.AddCommand(cmdApp)
+}
+
+/*
+dead-tooter app create --client-name ""
+*/
+
+var cmdApp = &cobra.Command{
+ Use: "application",
+ Short: "Register and perform application-level actions",
+ Long: "application, man",
+ Run: func(cmd *cobra.Command, args []string) {
+ },
+}
--- /dev/null
+package tooter
+
+import (
+ "git.vexingworkshop.com/signal9/dead-tooter/pkg/mastodon"
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ rootCmd.AddCommand(loginCmd)
+}
+
+var loginCmd = &cobra.Command{
+ Use: "login",
+ Short: "Login to Mastodon",
+ Long: "Initiate login to your Mastodon server of choice",
+ Run: func(cmd *cobra.Command, args []string) {
+ },
+}
+
+func login() {
+ app, err := mastodon.Load("dead-tooter")
+ if err != nil {
+ client := mastodon.Client{
+ ClientName: "dead-tooter",
+ RedirectUris: mastodon.RedirectUris,
+ Scopes: "read write follow push",
+ Website: "https://dead-tooter.vexingworkshop.com",
+ }
+ app, err = mastodon.Create(client)
+ if err != nil {
+ panic(err.Error())
+ }
+ app.Save()
+ }
+
+ var account mastodon.Account
+ code := account.Authorize(app)
+ token, err := account.RequestToken(app, code)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ err = account.VerifyCredentials(token)
+ if err != nil {
+ panic(err.Error())
+ }
+}
--- /dev/null
+package tooter
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/spf13/cobra"
+)
+
+var rootCmd = &cobra.Command{
+ Use: "dead-tooter",
+ Short: "A CLI for Mastodon hate scripts",
+ Long: `Provides a collection of capabilities that may or may not
+ be present in the Mastodon web UI`,
+
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("d34d-t00t3r")
+ },
+}
+
+func Execute() {
+ if err := rootCmd.Execute(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
--- /dev/null
+package tooter
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ rootCmd.AddCommand(versionCmd)
+}
+
+var versionCmd = &cobra.Command{
+ Use: "version",
+ Short: "Print the version number of d34d-t00ter",
+ Long: "Version, man, version!",
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("d34d-t00ter, Mastodon hate CLI v0.1")
+ },
+}
module git.vexingworkshop.com/signal9/dead-tooter
go 1.18
+
+require github.com/spf13/cobra v1.6.1
+
+require (
+ github.com/inconshreveable/mousetrap v1.0.1 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+)
--- /dev/null
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
+github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+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=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package main
import (
- "fmt"
-
- "git.vexingworkshop.com/signal9/dead-tooter/pkg/mastodon"
+ cmd "git.vexingworkshop.com/signal9/dead-tooter/cmd"
)
func main() {
- app := mastodon.Application{}
- err := app.Load("dead-tooter")
- if err != nil {
- panic(err.Error())
- }
-
- token, err := app.Login()
- if err != nil {
- panic(err.Error())
- }
-
- err = app.VerifyCredentials(token)
- if err != nil {
- panic(err.Error())
- }
-
- var account mastodon.Account
- code := account.Authorize(app)
- token, err = account.RequestToken(app, code)
- if err != nil {
- panic(err.Error())
- }
-
- err = account.VerifyCredentials(token)
- if err != nil {
- panic(err.Error())
- }
-
- followers, err := account.GetFollowers(token)
- if err != nil {
- panic(err.Error())
- }
-
- fmt.Printf("%+v\n", followers)
+ cmd.Execute()
}
+++ /dev/null
-package mastodon
-
-import (
- "encoding/json"
- "io"
- "net/http"
- "net/url"
-)
-
-// RedirectUris when passed to the redirect_uris parameter, will
-// show return the authorization code instead of redirecting the client.
-const RedirectUris = "urn:ietf:wg:oauth:2.0:oob"
-
-// App represents the basic and methods necessary to register an application
-// with a Mastodon server.
-type App struct {
- ClientName string `json:"client_name"`
- RedirectUris string `json:"redirect_uris"`
- Scopes string `json:"scopes"`
- Website string `json:"website"`
-}
-
-// Create calls the Mastodon API to register the App. It returns an Application
-// instance.
-//
-// app, err := mastodon.App{
-// ClientName: "dead-tooter",
-// RedirectUris: mastodon.RedirectUris,
-// Scopes: "read write follow push",
-// Website: "https://dead-tooter.vexingworkshop.com",
-// }.Create()
-func (a *App) Create() (application Application, err error) {
- resp, err := http.PostForm("https://hackers.town/api/v1/apps",
- url.Values{
- "client_name": {a.ClientName},
- "redirect_uris": {a.RedirectUris},
- "scopes": {a.Scopes},
- "website": {a.Website},
- },
- )
- if err != nil {
- panic(err.Error())
- }
- defer resp.Body.Close()
-
- body, err := io.ReadAll(resp.Body)
- err = json.Unmarshal(body, &application)
-
- return
-}
const configdir = "foodir/"
const mastohost = "hackers.town"
+// RedirectUris when passed to the redirect_uris parameter, will
+// show return the authorization code instead of redirecting the client.
+const RedirectUris = "urn:ietf:wg:oauth:2.0:oob"
+
// Token struct contains the data returned by the Application login request.
type Token struct {
AccessToken string `json:"access_token"`
VapidKey string `json:"vapid_key,omitempty"`
}
-// Save will store a serialized version of the Application struct to a file.
-func (a *Application) Save() {
- err := os.Mkdir(configdir, 0750)
- if err != nil && !os.IsExist(err) {
- panic(err.Error())
- }
+// Client represents the basic stanza required to register an application
+// with the Mastodon API
+type Client struct {
+ ClientName string `json:"client_name"`
+ RedirectUris string `json:"redirect_uris"`
+ Scopes string `json:"scopes"`
+ Website string `json:"website"`
+}
- data, err := json.Marshal(a)
- if err != nil {
- panic(err.Error())
+// Create calls the Mastodon API to regsiter the application.
+func Create(client Client) (app Application, err error) {
+ v := url.Values{}
+ v.Set("client_name", client.ClientName)
+ v.Set("redirect_uris", client.RedirectUris)
+ v.Set("scopes", client.Scopes)
+ v.Set("website", client.Website)
+
+ u := url.URL{
+ Host: mastohost,
+ Path: "oauth/token",
+ RawQuery: v.Encode(),
}
+ u.Scheme = "https"
- err = os.WriteFile(configdir+a.Name, data, 0666)
+ resp, err := http.PostForm(u.String(), v)
if err != nil {
panic(err.Error())
}
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ err = json.Unmarshal(body, &app)
+
+ return
}
// Load will hydrate an Application instance based upon data stored in
// a file.
-func (a *Application) Load(name string) error {
+func Load(name string) (app Application, err error) {
data, err := os.ReadFile(configdir + name)
if err != nil && os.IsNotExist(err) {
+ return
+ }
+
+ err = json.Unmarshal(data, &app)
+ if err != nil {
+ return
+ }
+
+ return app, nil
+}
+
+// Save will store a serialized version of the Application struct to a file.
+func (a *Application) Save() {
+ err := os.Mkdir(configdir, 0750)
+ if err != nil && !os.IsExist(err) {
panic(err.Error())
}
- err = json.Unmarshal(data, a)
+ data, err := json.Marshal(a)
if err != nil {
panic(err.Error())
}
- return nil
+ err = os.WriteFile(configdir+a.Name, data, 0666)
+ if err != nil {
+ panic(err.Error())
+ }
}
// Login authenticates the application to the Mastodon API, returning