From 2542bf46bae32f3c414e83a2aab0c0e3f8480064 Mon Sep 17 00:00:00 2001 From: gutmet Date: Sat, 19 Nov 2022 20:02:47 +0100 Subject: [PATCH] remove dependencies and client --- LICENSE | 1 + README.md | 6 +- cmd/mstdn/README.md | 52 ---- cmd/mstdn/cmd_account.go | 30 --- cmd/mstdn/cmd_account_test.go | 30 --- cmd/mstdn/cmd_delete.go | 23 -- cmd/mstdn/cmd_delete_test.go | 41 --- cmd/mstdn/cmd_follow.go | 30 --- cmd/mstdn/cmd_follow_test.go | 70 ----- cmd/mstdn/cmd_followers.go | 40 --- cmd/mstdn/cmd_followers_test.go | 34 --- cmd/mstdn/cmd_instance.go | 44 --- cmd/mstdn/cmd_instance_activity.go | 24 -- cmd/mstdn/cmd_instance_peers.go | 21 -- cmd/mstdn/cmd_instance_test.go | 30 --- cmd/mstdn/cmd_mikami.go | 9 - cmd/mstdn/cmd_mikami_test.go | 41 --- cmd/mstdn/cmd_notification.go | 29 -- cmd/mstdn/cmd_notification_test.go | 30 --- cmd/mstdn/cmd_search.go | 47 ---- cmd/mstdn/cmd_search_test.go | 32 --- cmd/mstdn/cmd_stream.go | 113 -------- cmd/mstdn/cmd_stream_test.go | 84 ------ cmd/mstdn/cmd_test.go | 41 --- cmd/mstdn/cmd_timeline.go | 68 ----- cmd/mstdn/cmd_timeline_test.go | 52 ---- cmd/mstdn/cmd_toot.go | 33 --- cmd/mstdn/cmd_toot_test.go | 52 ---- cmd/mstdn/cmd_upload.go | 33 --- cmd/mstdn/cmd_upload_test.go | 30 --- cmd/mstdn/cmd_xsearch.go | 38 --- cmd/mstdn/cmd_xsearch_test.go | 76 ------ cmd/mstdn/go.mod | 14 - cmd/mstdn/go.sum | 55 ---- cmd/mstdn/main.go | 411 ----------------------------- cmd/mstdn/main_test.go | 135 ---------- go.mod | 5 - go.sum | 4 - mastodon.go | 44 ++- streaming_ws.go | 195 -------------- streaming_ws_test.go | 281 -------------------- 41 files changed, 23 insertions(+), 2405 deletions(-) delete mode 100644 cmd/mstdn/README.md delete mode 100644 cmd/mstdn/cmd_account.go delete mode 100644 cmd/mstdn/cmd_account_test.go delete mode 100644 cmd/mstdn/cmd_delete.go delete mode 100644 cmd/mstdn/cmd_delete_test.go delete mode 100644 cmd/mstdn/cmd_follow.go delete mode 100644 cmd/mstdn/cmd_follow_test.go delete mode 100644 cmd/mstdn/cmd_followers.go delete mode 100644 cmd/mstdn/cmd_followers_test.go delete mode 100644 cmd/mstdn/cmd_instance.go delete mode 100644 cmd/mstdn/cmd_instance_activity.go delete mode 100644 cmd/mstdn/cmd_instance_peers.go delete mode 100644 cmd/mstdn/cmd_instance_test.go delete mode 100644 cmd/mstdn/cmd_mikami.go delete mode 100644 cmd/mstdn/cmd_mikami_test.go delete mode 100644 cmd/mstdn/cmd_notification.go delete mode 100644 cmd/mstdn/cmd_notification_test.go delete mode 100644 cmd/mstdn/cmd_search.go delete mode 100644 cmd/mstdn/cmd_search_test.go delete mode 100644 cmd/mstdn/cmd_stream.go delete mode 100644 cmd/mstdn/cmd_stream_test.go delete mode 100644 cmd/mstdn/cmd_test.go delete mode 100644 cmd/mstdn/cmd_timeline.go delete mode 100644 cmd/mstdn/cmd_timeline_test.go delete mode 100644 cmd/mstdn/cmd_toot.go delete mode 100644 cmd/mstdn/cmd_toot_test.go delete mode 100644 cmd/mstdn/cmd_upload.go delete mode 100644 cmd/mstdn/cmd_upload_test.go delete mode 100644 cmd/mstdn/cmd_xsearch.go delete mode 100644 cmd/mstdn/cmd_xsearch_test.go delete mode 100644 cmd/mstdn/go.mod delete mode 100644 cmd/mstdn/go.sum delete mode 100644 cmd/mstdn/main.go delete mode 100644 cmd/mstdn/main_test.go delete mode 100644 streaming_ws.go delete mode 100644 streaming_ws_test.go diff --git a/LICENSE b/LICENSE index 42066c7..2d0f7bc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ MIT License +Copyright (c) 2022 Alexander Weinhold Copyright (c) 2017 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/README.md b/README.md index 989d6e4..e221924 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # go-mastodon -[![Build Status](https://github.com/mattn/go-mastodon/workflows/test/badge.svg?branch=master)](https://github.com/mattn/go-mastodon/actions?query=workflow%3Atest) -[![Codecov](https://codecov.io/gh/mattn/go-mastodon/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-mastodon) -[![Go Reference](https://pkg.go.dev/badge/github.com/mattn/go-mastodon.svg)](https://pkg.go.dev/github.com/mattn/go-mastodon) -[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-mastodon)](https://goreportcard.com/report/github.com/mattn/go-mastodon) - +Fork of https://github.com/mattn/go-mastodon ## Usage diff --git a/cmd/mstdn/README.md b/cmd/mstdn/README.md deleted file mode 100644 index cc6f01c..0000000 --- a/cmd/mstdn/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# mstdn - -command line tool for mstdn.jp - -## Usage - -``` -NAME: - mstdn - mastodon client - -USAGE: - mstdn [global options] command [command options] [arguments...] - -VERSION: - 0.0.1 - -COMMANDS: - toot post toot - stream stream statuses - timeline show timeline - notification show notification - instance show instance information - account show account information - search search content - follow follow account - followers show followers - upload upload file - delete delete status - init initialize profile - mikami search mikami - xsearch cross search - help, h Shows a list of commands or help for one command - -GLOBAL OPTIONS: - --profile value profile name - --help, -h show help - --version, -v print the version -``` - -## Installation - -``` -$ go get github.com/mattn/go-mastodon/cmd/mstdn -``` - -## License - -MIT - -## Author - -Yasuhiro Matsumoto (a.k.a. mattn) diff --git a/cmd/mstdn/cmd_account.go b/cmd/mstdn/cmd_account.go deleted file mode 100644 index 23083c2..0000000 --- a/cmd/mstdn/cmd_account.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "context" - "fmt" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdAccount(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - account, err := client.GetAccountCurrentUser(context.Background()) - if err != nil { - return err - } - fmt.Fprintf(c.App.Writer, "URI : %v\n", account.Acct) - fmt.Fprintf(c.App.Writer, "ID : %v\n", account.ID) - fmt.Fprintf(c.App.Writer, "Username : %v\n", account.Username) - fmt.Fprintf(c.App.Writer, "Acct : %v\n", account.Acct) - fmt.Fprintf(c.App.Writer, "DisplayName : %v\n", account.DisplayName) - fmt.Fprintf(c.App.Writer, "Locked : %v\n", account.Locked) - fmt.Fprintf(c.App.Writer, "CreatedAt : %v\n", account.CreatedAt.Local()) - fmt.Fprintf(c.App.Writer, "FollowersCount: %v\n", account.FollowersCount) - fmt.Fprintf(c.App.Writer, "FollowingCount: %v\n", account.FollowingCount) - fmt.Fprintf(c.App.Writer, "StatusesCount : %v\n", account.StatusesCount) - fmt.Fprintf(c.App.Writer, "Note : %v\n", textContent(account.Note)) - fmt.Fprintf(c.App.Writer, "URL : %v\n", account.URL) - return nil -} diff --git a/cmd/mstdn/cmd_account_test.go b/cmd/mstdn/cmd_account_test.go deleted file mode 100644 index 8894451..0000000 --- a/cmd/mstdn/cmd_account_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdAccount(t *testing.T) { - out := testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/accounts/verify_credentials": - fmt.Fprintln(w, `{"username": "zzz"}`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - app.Run([]string{"mstdn", "account"}) - }, - ) - if !strings.Contains(out, "zzz") { - t.Fatalf("%q should be contained in output of command: %v", "zzz", out) - } -} diff --git a/cmd/mstdn/cmd_delete.go b/cmd/mstdn/cmd_delete.go deleted file mode 100644 index 092bad6..0000000 --- a/cmd/mstdn/cmd_delete.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "context" - "errors" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdDelete(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - if !c.Args().Present() { - return errors.New("arguments required") - } - for i := 0; i < c.NArg(); i++ { - err := client.DeleteStatus(context.Background(), mastodon.ID(c.Args().Get(i))) - if err != nil { - return err - } - } - return nil -} diff --git a/cmd/mstdn/cmd_delete_test.go b/cmd/mstdn/cmd_delete_test.go deleted file mode 100644 index e37442d..0000000 --- a/cmd/mstdn/cmd_delete_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdDelete(t *testing.T) { - ok := false - f := func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/statuses/123": - fmt.Fprintln(w, `{}`) - ok = true - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - } - testWithServer( - f, func(app *cli.App) { - app.Run([]string{"mstdn", "delete", "122"}) - }, - ) - if ok { - t.Fatal("something wrong to sequence to follow account") - } - - ok = false - testWithServer( - f, func(app *cli.App) { - app.Run([]string{"mstdn", "delete", "123"}) - }, - ) - if !ok { - t.Fatal("something wrong to sequence to follow account") - } -} diff --git a/cmd/mstdn/cmd_follow.go b/cmd/mstdn/cmd_follow.go deleted file mode 100644 index e064f03..0000000 --- a/cmd/mstdn/cmd_follow.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "context" - "errors" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdFollow(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - if !c.Args().Present() { - return errors.New("arguments required") - } - for i := 0; i < c.NArg(); i++ { - account, err := client.AccountsSearch(context.Background(), c.Args().Get(i), 1) - if err != nil { - return err - } - if len(account) == 0 { - continue - } - _, err = client.AccountFollow(context.Background(), account[0].ID) - if err != nil { - return err - } - } - return nil -} diff --git a/cmd/mstdn/cmd_follow_test.go b/cmd/mstdn/cmd_follow_test.go deleted file mode 100644 index e4f43c9..0000000 --- a/cmd/mstdn/cmd_follow_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdFollow(t *testing.T) { - ok := false - testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/accounts/search": - q := r.URL.Query().Get("q") - if q == "mattn" { - fmt.Fprintln(w, `[{"id": 123}]`) - return - } else if q == "different_id" { - fmt.Fprintln(w, `[{"id": 1234567}]`) - return - } else if q == "empty" { - fmt.Fprintln(w, `[]`) - return - } - case "/api/v1/accounts/123/follow": - fmt.Fprintln(w, `{"id": 123}`) - ok = true - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - err := app.Run([]string{"mstdn", "follow", "mattn"}) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - }, - func(app *cli.App) { - err := app.Run([]string{"mstdn", "follow"}) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - }, - func(app *cli.App) { - err := app.Run([]string{"mstdn", "follow", "fail"}) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - }, - func(app *cli.App) { - err := app.Run([]string{"mstdn", "follow", "empty"}) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - }, - func(app *cli.App) { - err := app.Run([]string{"mstdn", "follow", "different_id"}) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - }, - ) - if !ok { - t.Fatal("something wrong to sequence to follow account") - } -} diff --git a/cmd/mstdn/cmd_followers.go b/cmd/mstdn/cmd_followers.go deleted file mode 100644 index bfacf8b..0000000 --- a/cmd/mstdn/cmd_followers.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "context" - "fmt" - "time" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdFollowers(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - config := c.App.Metadata["config"].(*mastodon.Config) - - account, err := client.GetAccountCurrentUser(context.Background()) - if err != nil { - return err - } - var followers []*mastodon.Account - var pg mastodon.Pagination - for { - fs, err := client.GetAccountFollowers(context.Background(), account.ID, &pg) - if err != nil { - return err - } - followers = append(followers, fs...) - if pg.MaxID == "" { - break - } - pg.SinceID = "" - pg.MinID = "" - time.Sleep(10 * time.Second) - } - s := newScreen(config) - for _, follower := range followers { - fmt.Fprintf(c.App.Writer, "%v,%v\n", follower.ID, s.acct(follower.Acct)) - } - return nil -} diff --git a/cmd/mstdn/cmd_followers_test.go b/cmd/mstdn/cmd_followers_test.go deleted file mode 100644 index f8c5080..0000000 --- a/cmd/mstdn/cmd_followers_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdFollowers(t *testing.T) { - out := testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/accounts/verify_credentials": - fmt.Fprintln(w, `{"id": 123}`) - return - case "/api/v1/accounts/123/followers": - w.Header().Set("Link", `; rel="prev"`) - fmt.Fprintln(w, `[{"id": 234, "username": "ZZZ", "acct": "zzz"}]`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - app.Run([]string{"mstdn", "followers"}) - }, - ) - if !strings.Contains(out, "zzz") { - t.Fatalf("%q should be contained in output of command: %v", "zzz", out) - } -} diff --git a/cmd/mstdn/cmd_instance.go b/cmd/mstdn/cmd_instance.go deleted file mode 100644 index 5932c68..0000000 --- a/cmd/mstdn/cmd_instance.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "context" - "fmt" - "sort" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdInstance(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - instance, err := client.GetInstance(context.Background()) - if err != nil { - return err - } - fmt.Fprintf(c.App.Writer, "URI : %s\n", instance.URI) - fmt.Fprintf(c.App.Writer, "Title : %s\n", instance.Title) - fmt.Fprintf(c.App.Writer, "Description: %s\n", instance.Description) - fmt.Fprintf(c.App.Writer, "EMail : %s\n", instance.EMail) - if instance.Version != "" { - fmt.Fprintf(c.App.Writer, "Version : %s\n", instance.Version) - } - if instance.Thumbnail != "" { - fmt.Fprintf(c.App.Writer, "Thumbnail : %s\n", instance.Thumbnail) - } - if instance.URLs != nil { - var keys []string - for _, k := range instance.URLs { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - fmt.Fprintf(c.App.Writer, "%s: %s\n", k, instance.URLs[k]) - } - } - if instance.Stats != nil { - fmt.Fprintf(c.App.Writer, "User Count : %v\n", instance.Stats.UserCount) - fmt.Fprintf(c.App.Writer, "Status Count : %v\n", instance.Stats.StatusCount) - fmt.Fprintf(c.App.Writer, "Domain Count : %v\n", instance.Stats.DomainCount) - } - return nil -} diff --git a/cmd/mstdn/cmd_instance_activity.go b/cmd/mstdn/cmd_instance_activity.go deleted file mode 100644 index 199ba99..0000000 --- a/cmd/mstdn/cmd_instance_activity.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "context" - "fmt" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdInstanceActivity(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - activities, err := client.GetInstanceActivity(context.Background()) - if err != nil { - return err - } - for _, activity := range activities { - fmt.Fprintf(c.App.Writer, "Logins : %v\n", activity.Logins) - fmt.Fprintf(c.App.Writer, "Registrations : %v\n", activity.Registrations) - fmt.Fprintf(c.App.Writer, "Statuses : %v\n", activity.Statuses) - fmt.Fprintf(c.App.Writer, "Week : %v\n", activity.Week) - } - return nil -} diff --git a/cmd/mstdn/cmd_instance_peers.go b/cmd/mstdn/cmd_instance_peers.go deleted file mode 100644 index 7a73d1d..0000000 --- a/cmd/mstdn/cmd_instance_peers.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "context" - "fmt" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdInstancePeers(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - peers, err := client.GetInstancePeers(context.Background()) - if err != nil { - return err - } - for _, peer := range peers { - fmt.Fprintln(c.App.Writer, peer) - } - return nil -} diff --git a/cmd/mstdn/cmd_instance_test.go b/cmd/mstdn/cmd_instance_test.go deleted file mode 100644 index f5a6279..0000000 --- a/cmd/mstdn/cmd_instance_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdInstance(t *testing.T) { - out := testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/instance": - fmt.Fprintln(w, `{"title": "zzz"}`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - app.Run([]string{"mstdn", "instance"}) - }, - ) - if !strings.Contains(out, "zzz") { - t.Fatalf("%q should be contained in output of command: %v", "zzz", out) - } -} diff --git a/cmd/mstdn/cmd_mikami.go b/cmd/mstdn/cmd_mikami.go deleted file mode 100644 index abbfc18..0000000 --- a/cmd/mstdn/cmd_mikami.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "github.com/urfave/cli" -) - -func cmdMikami(c *cli.Context) error { - return xSearch(c.App.Metadata["xsearch_url"].(string), "三上", c.App.Writer) -} diff --git a/cmd/mstdn/cmd_mikami_test.go b/cmd/mstdn/cmd_mikami_test.go deleted file mode 100644 index 3fe9eae..0000000 --- a/cmd/mstdn/cmd_mikami_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "net/http" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdMikami(t *testing.T) { - ok := false - buf := bytes.NewBuffer(nil) - testWithServer( - func(w http.ResponseWriter, r *http.Request) { - if r.URL.Query().Get("q") == "三上" { - ok = true - fmt.Fprintln(w, `
`) - } - }, - func(app *cli.App) { - app.Writer = buf - err := app.Run([]string{"mstdn", "mikami"}) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - }, - ) - if !ok { - t.Fatal("should be search Mikami") - } - result := buf.String() - if !strings.Contains(result, "http://example.com/@test/1") { - t.Fatalf("%q should be contained in output of search: %s", "http://example.com/@test/1", result) - } - if !strings.Contains(result, "三上") { - t.Fatalf("%q should be contained in output of search: %s", "三上", result) - } -} diff --git a/cmd/mstdn/cmd_notification.go b/cmd/mstdn/cmd_notification.go deleted file mode 100644 index b32ba4e..0000000 --- a/cmd/mstdn/cmd_notification.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "context" - "fmt" - - "github.com/fatih/color" - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdNotification(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - notifications, err := client.GetNotifications(context.Background(), nil) - if err != nil { - return err - } - for _, n := range notifications { - if n.Status != nil { - color.Set(color.FgHiRed) - fmt.Fprint(c.App.Writer, n.Account.Acct) - color.Set(color.Reset) - fmt.Fprintln(c.App.Writer, " "+n.Type) - s := n.Status - fmt.Fprintln(c.App.Writer, textContent(s.Content)) - } - } - return nil -} diff --git a/cmd/mstdn/cmd_notification_test.go b/cmd/mstdn/cmd_notification_test.go deleted file mode 100644 index 25a9429..0000000 --- a/cmd/mstdn/cmd_notification_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdNotification(t *testing.T) { - out := testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/notifications": - fmt.Fprintln(w, `[{"type": "rebloged", "status": {"content": "foo"}}]`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - app.Run([]string{"mstdn", "notification"}) - }, - ) - if !strings.Contains(out, "rebloged") { - t.Fatalf("%q should be contained in output of command: %v", "rebloged", out) - } -} diff --git a/cmd/mstdn/cmd_search.go b/cmd/mstdn/cmd_search.go deleted file mode 100644 index 6d7e81b..0000000 --- a/cmd/mstdn/cmd_search.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "context" - "errors" - "fmt" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdSearch(c *cli.Context) error { - if !c.Args().Present() { - return errors.New("arguments required") - } - - client := c.App.Metadata["client"].(*mastodon.Client) - config := c.App.Metadata["config"].(*mastodon.Config) - - results, err := client.Search(context.Background(), argstr(c), false) - if err != nil { - return err - } - s := newScreen(config) - if len(results.Accounts) > 0 { - fmt.Fprintln(c.App.Writer, "===ACCOUNT===") - for _, result := range results.Accounts { - fmt.Fprintf(c.App.Writer, "%v,%v\n", result.ID, s.acct(result.Acct)) - } - fmt.Fprintln(c.App.Writer) - } - if len(results.Statuses) > 0 { - fmt.Fprintln(c.App.Writer, "===STATUS===") - for _, result := range results.Statuses { - s.displayStatus(c.App.Writer, result) - } - fmt.Fprintln(c.App.Writer) - } - if len(results.Hashtags) > 0 { - fmt.Fprintln(c.App.Writer, "===HASHTAG===") - for _, result := range results.Hashtags { - fmt.Fprintf(c.App.Writer, "#%v\n", result) - } - fmt.Fprintln(c.App.Writer) - } - return nil -} diff --git a/cmd/mstdn/cmd_search_test.go b/cmd/mstdn/cmd_search_test.go deleted file mode 100644 index f3509cc..0000000 --- a/cmd/mstdn/cmd_search_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdSearch(t *testing.T) { - out := testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v2/search": - fmt.Fprintln(w, `{"accounts": [{"id": 234, "acct": "zzz"}], "statuses":[{"id": 345, "content": "yyy"}], "hashtags": [{"name": "www"}, {"name": "わろす"}]}`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - app.Run([]string{"mstdn", "search", "zzz"}) - }, - ) - for _, s := range []string{"zzz", "yyy", "www", "わろす"} { - if !strings.Contains(out, s) { - t.Fatalf("%q should be contained in output of command: %v", s, out) - } - } -} diff --git a/cmd/mstdn/cmd_stream.go b/cmd/mstdn/cmd_stream.go deleted file mode 100644 index 9626769..0000000 --- a/cmd/mstdn/cmd_stream.go +++ /dev/null @@ -1,113 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "errors" - "os" - "os/signal" - "strings" - "text/template" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -// SimpleJSON is a struct for output JSON for data to be simple used -type SimpleJSON struct { - ID mastodon.ID `json:"id"` - Username string `json:"username"` - Acct string `json:"acct"` - Avatar string `json:"avatar"` - Content string `json:"content"` -} - -func checkFlag(f ...bool) bool { - n := 0 - for _, on := range f { - if on { - n++ - } - } - return n > 1 -} - -func cmdStream(c *cli.Context) error { - asJSON := c.Bool("json") - asSimpleJSON := c.Bool("simplejson") - asFormat := c.String("template") - - if checkFlag(asJSON, asSimpleJSON, asFormat != "") { - return errors.New("cannot speicify two or three options in --json/--simplejson/--template") - } - tx, err := template.New("mstdn").Funcs(template.FuncMap{ - "nl": func(s string) string { - return s + "\n" - }, - "text": textContent, - }).Parse(asFormat) - if err != nil { - return err - } - - client := c.App.Metadata["client"].(*mastodon.Client) - config := c.App.Metadata["config"].(*mastodon.Config) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - sc := make(chan os.Signal, 1) - signal.Notify(sc, os.Interrupt) - - var q chan mastodon.Event - - t := c.String("type") - if t == "public" { - q, err = client.StreamingPublic(ctx, false) - } else if t == "" || t == "public/local" { - q, err = client.StreamingPublic(ctx, true) - } else if strings.HasPrefix(t, "user:") { - q, err = client.StreamingUser(ctx) - } else if strings.HasPrefix(t, "hashtag:") { - q, err = client.StreamingHashtag(ctx, t[8:], false) - } else { - return errors.New("invalid type") - } - if err != nil { - return err - } - go func() { - <-sc - cancel() - }() - - c.App.Metadata["signal"] = sc - - s := newScreen(config) - for e := range q { - if asJSON { - json.NewEncoder(c.App.Writer).Encode(e) - } else if asSimpleJSON { - if t, ok := e.(*mastodon.UpdateEvent); ok { - json.NewEncoder(c.App.Writer).Encode(&SimpleJSON{ - ID: t.Status.ID, - Username: t.Status.Account.Username, - Acct: t.Status.Account.Acct, - Avatar: t.Status.Account.AvatarStatic, - Content: textContent(t.Status.Content), - }) - } - } else if asFormat != "" { - tx.ExecuteTemplate(c.App.Writer, "mstdn", e) - } else { - switch t := e.(type) { - case *mastodon.UpdateEvent: - s.displayStatus(c.App.Writer, t.Status) - case *mastodon.NotificationEvent: - // TODO s.displayStatus(c.App.Writer, t.Notification.Status) - case *mastodon.ErrorEvent: - s.displayError(c.App.Writer, t) - } - } - } - return nil -} diff --git a/cmd/mstdn/cmd_stream_test.go b/cmd/mstdn/cmd_stream_test.go deleted file mode 100644 index b3b37c7..0000000 --- a/cmd/mstdn/cmd_stream_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "net/http" - "net/http/httptest" - "os" - "strings" - "testing" - "time" - - "github.com/mattn/go-mastodon" -) - -func TestCmdStream(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/api/v1/streaming/public/local" { - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - } - f, _ := w.(http.Flusher) - fmt.Fprintln(w, ` -event: update -data: {"content": "foo", "account":{"acct":"FOO"}} - `) - f.Flush() - - fmt.Fprintln(w, ` -event: update -data: {"content": "bar", "account":{"acct":"BAR"}} - `) - f.Flush() - return - })) - defer ts.Close() - - config := &mastodon.Config{ - Server: ts.URL, - ClientID: "foo", - ClientSecret: "bar", - AccessToken: "zoo", - } - client := mastodon.NewClient(config) - - var buf bytes.Buffer - app := makeApp() - app.Writer = &buf - app.Metadata = map[string]interface{}{ - "client": client, - "config": config, - } - - stop := func() { - time.Sleep(5 * time.Second) - if sig, ok := app.Metadata["signal"]; ok { - sig.(chan os.Signal) <- os.Interrupt - return - } - panic("timeout") - } - - var out string - - go stop() - app.Run([]string{"mstdn", "stream"}) - out = buf.String() - if !strings.Contains(out, "FOO@") { - t.Fatalf("%q should be contained in output of command: %v", "FOO@", out) - } - if !strings.Contains(out, "foo") { - t.Fatalf("%q should be contained in output of command: %v", "foo", out) - } - - go stop() - app.Run([]string{"mstdn", "stream", "--simplejson"}) - out = buf.String() - if !strings.Contains(out, "FOO@") { - t.Fatalf("%q should be contained in output of command: %v", "FOO@", out) - } - if !strings.Contains(out, "foo") { - t.Fatalf("%q should be contained in output of command: %v", "foo", out) - } -} diff --git a/cmd/mstdn/cmd_test.go b/cmd/mstdn/cmd_test.go deleted file mode 100644 index 14043c3..0000000 --- a/cmd/mstdn/cmd_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "bytes" - "net/http" - "net/http/httptest" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func testWithServer(h http.HandlerFunc, testFuncs ...func(*cli.App)) string { - ts := httptest.NewServer(h) - defer ts.Close() - - cli.OsExiter = func(n int) {} - - client := mastodon.NewClient(&mastodon.Config{ - Server: ts.URL, - ClientID: "foo", - ClientSecret: "bar", - AccessToken: "zoo", - }) - - var buf bytes.Buffer - app := makeApp() - app.Writer = &buf - app.Metadata = map[string]interface{}{ - "client": client, - "config": &mastodon.Config{ - Server: "https://example.com", - }, - "xsearch_url": ts.URL, - } - - for _, f := range testFuncs { - f(app) - } - - return buf.String() -} diff --git a/cmd/mstdn/cmd_timeline.go b/cmd/mstdn/cmd_timeline.go deleted file mode 100644 index c610966..0000000 --- a/cmd/mstdn/cmd_timeline.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import ( - "context" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdTimeline(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - config := c.App.Metadata["config"].(*mastodon.Config) - timeline, err := client.GetTimelineHome(context.Background(), nil) - if err != nil { - return err - } - s := newScreen(config) - for i := len(timeline) - 1; i >= 0; i-- { - s.displayStatus(c.App.Writer, timeline[i]) - } - return nil -} - -func cmdTimelineHome(c *cli.Context) error { - return cmdTimeline(c) -} - -func cmdTimelinePublic(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - config := c.App.Metadata["config"].(*mastodon.Config) - timeline, err := client.GetTimelinePublic(context.Background(), false, nil) - if err != nil { - return err - } - s := newScreen(config) - for i := len(timeline) - 1; i >= 0; i-- { - s.displayStatus(c.App.Writer, timeline[i]) - } - return nil -} - -func cmdTimelineLocal(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - config := c.App.Metadata["config"].(*mastodon.Config) - timeline, err := client.GetTimelinePublic(context.Background(), true, nil) - if err != nil { - return err - } - s := newScreen(config) - for i := len(timeline) - 1; i >= 0; i-- { - s.displayStatus(c.App.Writer, timeline[i]) - } - return nil -} - -func cmdTimelineDirect(c *cli.Context) error { - client := c.App.Metadata["client"].(*mastodon.Client) - config := c.App.Metadata["config"].(*mastodon.Config) - timeline, err := client.GetTimelineDirect(context.Background(), nil) - if err != nil { - return err - } - s := newScreen(config) - for i := len(timeline) - 1; i >= 0; i-- { - s.displayStatus(c.App.Writer, timeline[i]) - } - return nil -} diff --git a/cmd/mstdn/cmd_timeline_test.go b/cmd/mstdn/cmd_timeline_test.go deleted file mode 100644 index d08a9fc..0000000 --- a/cmd/mstdn/cmd_timeline_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdTimeline(t *testing.T) { - out := testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/timelines/home": - fmt.Fprintln(w, `[{"content": "home"}]`) - return - case "/api/v1/timelines/public": - fmt.Fprintln(w, `[{"content": "public"}]`) - return - case "/api/v1/conversations": - fmt.Fprintln(w, `[{"id": "4", "unread":false, "last_status" : {"content": "direct"}}]`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - app.Run([]string{"mstdn", "timeline"}) - app.Run([]string{"mstdn", "timeline-home"}) - app.Run([]string{"mstdn", "timeline-public"}) - app.Run([]string{"mstdn", "timeline-local"}) - app.Run([]string{"mstdn", "timeline-direct"}) - }, - ) - want := strings.Join([]string{ - "@example.com", - "home", - "@example.com", - "home", - "@example.com", - "public", - "@example.com", - "public", - "@example.com", - "direct", - }, "\n") + "\n" - if !strings.Contains(out, want) { - t.Fatalf("%q should be contained in output of command: %v", want, out) - } -} diff --git a/cmd/mstdn/cmd_toot.go b/cmd/mstdn/cmd_toot.go deleted file mode 100644 index 7c2ee63..0000000 --- a/cmd/mstdn/cmd_toot.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "context" - "errors" - "fmt" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdToot(c *cli.Context) error { - var toot string - ff := c.String("ff") - if ff != "" { - text, err := readFile(ff) - if err != nil { - return err - } - toot = string(text) - } else { - if !c.Args().Present() { - return errors.New("arguments required") - } - toot = argstr(c) - } - client := c.App.Metadata["client"].(*mastodon.Client) - _, err := client.PostStatus(context.Background(), &mastodon.Toot{ - Status: toot, - InReplyToID: mastodon.ID(fmt.Sprint(c.String("i"))), - }) - return err -} diff --git a/cmd/mstdn/cmd_toot_test.go b/cmd/mstdn/cmd_toot_test.go deleted file mode 100644 index 4f997f0..0000000 --- a/cmd/mstdn/cmd_toot_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdToot(t *testing.T) { - toot := "" - testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/statuses": - toot = r.FormValue("status") - fmt.Fprintln(w, `{"id": 2345}`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - app.Run([]string{"mstdn", "toot", "foo"}) - }, - ) - if toot != "foo" { - t.Fatalf("want %q, got %q", "foo", toot) - } -} - -func TestCmdTootFileNotFound(t *testing.T) { - var err error - testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/statuses": - fmt.Fprintln(w, `{"id": 2345}`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - err = app.Run([]string{"mstdn", "toot", "-ff", "not-found"}) - }, - ) - if err == nil { - t.Fatal("should be fail") - } -} diff --git a/cmd/mstdn/cmd_upload.go b/cmd/mstdn/cmd_upload.go deleted file mode 100644 index 15f912e..0000000 --- a/cmd/mstdn/cmd_upload.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "context" - "errors" - "fmt" - - "github.com/mattn/go-mastodon" - "github.com/urfave/cli" -) - -func cmdUpload(c *cli.Context) error { - if !c.Args().Present() { - return errors.New("arguments required") - } - client := c.App.Metadata["client"].(*mastodon.Client) - for i := 0; i < c.NArg(); i++ { - attachment, err := client.UploadMedia(context.Background(), c.Args().Get(i)) - if err != nil { - return err - } - if i > 0 { - fmt.Fprintln(c.App.Writer) - } - fmt.Fprintf(c.App.Writer, "ID : %v\n", attachment.ID) - fmt.Fprintf(c.App.Writer, "Type : %v\n", attachment.Type) - fmt.Fprintf(c.App.Writer, "URL : %v\n", attachment.URL) - fmt.Fprintf(c.App.Writer, "RemoteURL : %v\n", attachment.RemoteURL) - fmt.Fprintf(c.App.Writer, "PreviewURL: %v\n", attachment.PreviewURL) - fmt.Fprintf(c.App.Writer, "TextURL : %v\n", attachment.TextURL) - } - return nil -} diff --git a/cmd/mstdn/cmd_upload_test.go b/cmd/mstdn/cmd_upload_test.go deleted file mode 100644 index 585d943..0000000 --- a/cmd/mstdn/cmd_upload_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdUpload(t *testing.T) { - out := testWithServer( - func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/api/v1/media": - fmt.Fprintln(w, `{"id": 123}`) - return - } - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - }, - func(app *cli.App) { - app.Run([]string{"mstdn", "upload", "../../testdata/logo.png"}) - }, - ) - if !strings.Contains(out, "123") { - t.Fatalf("%q should be contained in output of command: %v", "123", out) - } -} diff --git a/cmd/mstdn/cmd_xsearch.go b/cmd/mstdn/cmd_xsearch.go deleted file mode 100644 index 2c7950b..0000000 --- a/cmd/mstdn/cmd_xsearch.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "fmt" - "io" - "net/url" - - "github.com/PuerkitoBio/goquery" - "github.com/urfave/cli" -) - -func cmdXSearch(c *cli.Context) error { - return xSearch(c.App.Metadata["xsearch_url"].(string), c.Args().First(), c.App.Writer) -} - -func xSearch(xsearchRawurl, query string, w io.Writer) error { - u, err := url.Parse(xsearchRawurl) - if err != nil { - return err - } - params := url.Values{} - params.Set("q", query) - u.RawQuery = params.Encode() - doc, err := goquery.NewDocument(u.String()) - if err != nil { - return err - } - doc.Find(".post").Each(func(n int, elem *goquery.Selection) { - href, ok := elem.Find(".mst_content a").Attr("href") - if !ok { - return - } - text := elem.Find(".mst_content p").Text() - fmt.Fprintf(w, "%s\n", href) - fmt.Fprintf(w, "%s\n\n", text) - }) - return nil -} diff --git a/cmd/mstdn/cmd_xsearch_test.go b/cmd/mstdn/cmd_xsearch_test.go deleted file mode 100644 index f3dc105..0000000 --- a/cmd/mstdn/cmd_xsearch_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/urfave/cli" -) - -func TestCmdXSearch(t *testing.T) { - testWithServer( - func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, `
`) - }, - func(app *cli.App) { - err := app.Run([]string{"mstdn", "xsearch", "test"}) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - }, - ) -} - -func TestXSearch(t *testing.T) { - canErr := true - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if canErr { - canErr = false - http.Error(w, http.StatusText(http.StatusInternalServerError), 9999) - return - } else if r.URL.Query().Get("q") == "empty" { - fmt.Fprintln(w, `
`) - return - } - - fmt.Fprintln(w, `
`) - })) - defer ts.Close() - - err := xSearch(":", "", nil) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - - err = xSearch(ts.URL, "", nil) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - - buf := bytes.NewBuffer(nil) - err = xSearch(ts.URL, "empty", buf) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - result := buf.String() - if result != "" { - t.Fatalf("the search result should be empty: %s", result) - } - - buf = bytes.NewBuffer(nil) - err = xSearch(ts.URL, "test", buf) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - result = buf.String() - if !strings.Contains(result, "http://example.com/@test/1") { - t.Fatalf("%q should be contained in output of search: %s", "http://example.com/@test/1", result) - } - if !strings.Contains(result, "test status") { - t.Fatalf("%q should be contained in output of search: %s", "test status", result) - } -} diff --git a/cmd/mstdn/go.mod b/cmd/mstdn/go.mod deleted file mode 100644 index 729c262..0000000 --- a/cmd/mstdn/go.mod +++ /dev/null @@ -1,14 +0,0 @@ -module github.com/mattn/go-mastodon/cmd/mstdn - -go 1.16 - -replace github.com/mattn/go-mastodon => ../.. - -require ( - github.com/PuerkitoBio/goquery v1.8.0 - github.com/fatih/color v1.13.0 - github.com/mattn/go-mastodon v0.0.4 - github.com/mattn/go-tty v0.0.4 - github.com/urfave/cli v1.22.9 - golang.org/x/net v0.0.0-20220531201128-c960675eff93 -) diff --git a/cmd/mstdn/go.sum b/cmd/mstdn/go.sum deleted file mode 100644 index 2006e2f..0000000 --- a/cmd/mstdn/go.sum +++ /dev/null @@ -1,55 +0,0 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= -github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= -github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= -github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -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/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-tty v0.0.4 h1:NVikla9X8MN0SQAqCYzpGyXv0jY7MNl3HOWD2dkle7E= -github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -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/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw= -github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA= -golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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= diff --git a/cmd/mstdn/main.go b/cmd/mstdn/main.go deleted file mode 100644 index 141a364..0000000 --- a/cmd/mstdn/main.go +++ /dev/null @@ -1,411 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/url" - "os" - "path/filepath" - "runtime" - "strings" - - "github.com/fatih/color" - "github.com/mattn/go-mastodon" - "github.com/mattn/go-tty" - "github.com/urfave/cli" - "golang.org/x/net/html" -) - -func readFile(filename string) ([]byte, error) { - if filename == "-" { - return ioutil.ReadAll(os.Stdin) - } - return ioutil.ReadFile(filename) -} - -func textContent(s string) string { - doc, err := html.Parse(strings.NewReader(s)) - if err != nil { - return s - } - var buf bytes.Buffer - - var extractText func(node *html.Node, w *bytes.Buffer) - extractText = func(node *html.Node, w *bytes.Buffer) { - if node.Type == html.TextNode { - data := strings.Trim(node.Data, "\r\n") - if data != "" { - w.WriteString(data) - } - } - for c := node.FirstChild; c != nil; c = c.NextSibling { - extractText(c, w) - } - if node.Type == html.ElementNode { - name := strings.ToLower(node.Data) - if name == "br" { - w.WriteString("\n") - } - } - } - extractText(doc, &buf) - return buf.String() -} - -var ( - readUsername = func() (string, error) { - b, _, err := bufio.NewReader(os.Stdin).ReadLine() - if err != nil { - return "", err - } - return string(b), nil - } - readPassword func() (string, error) -) - -func prompt() (string, string, error) { - fmt.Print("E-Mail: ") - email, err := readUsername() - if err != nil { - return "", "", err - } - - fmt.Print("Password: ") - var password string - if readPassword == nil { - var t *tty.TTY - t, err = tty.Open() - if err != nil { - return "", "", err - } - defer t.Close() - password, err = t.ReadPassword() - } else { - password, err = readPassword() - } - if err != nil { - return "", "", err - } - return email, password, nil -} - -func configFile(c *cli.Context) (string, error) { - dir := os.Getenv("HOME") - if runtime.GOOS == "windows" { - dir = os.Getenv("APPDATA") - if dir == "" { - dir = filepath.Join(os.Getenv("USERPROFILE"), "Application Data", "mstdn") - } - dir = filepath.Join(dir, "mstdn") - } else { - dir = filepath.Join(dir, ".config", "mstdn") - } - if err := os.MkdirAll(dir, 0700); err != nil { - return "", err - } - var file string - profile := c.String("profile") - if profile != "" { - file = filepath.Join(dir, "settings-"+profile+".json") - } else { - file = filepath.Join(dir, "settings.json") - } - return file, nil -} - -func getConfig(c *cli.Context) (string, *mastodon.Config, error) { - file, err := configFile(c) - if err != nil { - return "", nil, err - } - b, err := ioutil.ReadFile(file) - if err != nil && !os.IsNotExist(err) { - return "", nil, err - } - config := &mastodon.Config{ - Server: "https://mstdn.jp", - ClientID: "1e463436008428a60ed14ff1f7bc0b4d923e14fc4a6827fa99560b0c0222612f", - ClientSecret: "72b63de5bc11111a5aa1a7b690672d78ad6a207ce32e16ea26115048ec5d234d", - } - if err == nil { - err = json.Unmarshal(b, &config) - if err != nil { - return "", nil, fmt.Errorf("could not unmarshal %v: %v", file, err) - } - } - return file, config, nil -} - -func authenticate(client *mastodon.Client, config *mastodon.Config, file string) error { - email, password, err := prompt() - if err != nil { - return err - } - err = client.Authenticate(context.Background(), email, password) - if err != nil { - return err - } - b, err := json.MarshalIndent(config, "", " ") - if err != nil { - return fmt.Errorf("failed to store file: %v", err) - } - err = ioutil.WriteFile(file, b, 0700) - if err != nil { - return fmt.Errorf("failed to store file: %v", err) - } - return nil -} - -func argstr(c *cli.Context) string { - a := []string{} - for i := 0; i < c.NArg(); i++ { - a = append(a, c.Args().Get(i)) - } - return strings.Join(a, " ") -} - -func fatalIf(err error) { - if err == nil { - return - } - fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], err) - os.Exit(1) -} - -func makeApp() *cli.App { - app := cli.NewApp() - app.Name = "mstdn" - app.Usage = "mastodon client" - app.Version = "0.0.1" - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "profile", - Usage: "profile name", - Value: "", - }, - } - app.Commands = []cli.Command{ - { - Name: "toot", - Usage: "post toot", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "ff", - Usage: "post utf-8 string from a file(\"-\" means STDIN)", - Value: "", - }, - cli.StringFlag{ - Name: "i", - Usage: "in-reply-to", - Value: "", - }, - }, - Action: cmdToot, - }, - { - Name: "stream", - Usage: "stream statuses", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "type", - Usage: "stream type (public,public/local,user:NAME,hashtag:TAG)", - }, - cli.BoolFlag{ - Name: "json", - Usage: "output JSON", - }, - cli.BoolFlag{ - Name: "simplejson", - Usage: "output simple JSON", - }, - cli.StringFlag{ - Name: "template", - Usage: "output with tamplate format", - }, - }, - Action: cmdStream, - }, - { - Name: "timeline", - Usage: "show timeline", - Action: cmdTimeline, - }, - { - Name: "timeline-home", - Usage: "show timeline home", - Action: cmdTimelineHome, - }, - { - Name: "timeline-local", - Usage: "show timeline local", - Action: cmdTimelineLocal, - }, - { - Name: "timeline-public", - Usage: "show timeline public", - Action: cmdTimelinePublic, - }, - { - Name: "timeline-direct", - Usage: "show timeline direct", - Action: cmdTimelineDirect, - }, - { - Name: "notification", - Usage: "show notification", - Action: cmdNotification, - }, - { - Name: "instance", - Usage: "show instance information", - Action: cmdInstance, - }, - { - Name: "instance_activity", - Usage: "show instance activity information", - Action: cmdInstanceActivity, - }, - { - Name: "instance_peers", - Usage: "show instance peers information", - Action: cmdInstancePeers, - }, - { - Name: "account", - Usage: "show account information", - Action: cmdAccount, - }, - { - Name: "search", - Usage: "search content", - Action: cmdSearch, - }, - { - Name: "follow", - Usage: "follow account", - Action: cmdFollow, - }, - { - Name: "followers", - Usage: "show followers", - Action: cmdFollowers, - }, - { - Name: "upload", - Usage: "upload file", - Action: cmdUpload, - }, - { - Name: "delete", - Usage: "delete status", - Action: cmdDelete, - }, - { - Name: "init", - Usage: "initialize profile", - Action: func(c *cli.Context) error { return nil }, - }, - { - Name: "mikami", - Usage: "search mikami", - Action: cmdMikami, - }, - { - Name: "xsearch", - Usage: "cross search", - Action: cmdXSearch, - }, - } - app.Setup() - return app -} - -type screen struct { - host string -} - -func newScreen(config *mastodon.Config) *screen { - var host string - u, err := url.Parse(config.Server) - if err == nil { - host = u.Host - } - return &screen{host} -} - -func (s *screen) acct(a string) string { - if !strings.Contains(a, "@") { - a += "@" + s.host - } - return a -} - -func (s *screen) displayError(w io.Writer, e error) { - color.Set(color.FgYellow) - fmt.Fprintln(w, e.Error()) - color.Set(color.Reset) -} - -func (s *screen) displayStatus(w io.Writer, t *mastodon.Status) { - if t == nil { - return - } - if t.Reblog != nil { - color.Set(color.FgHiRed) - fmt.Fprint(w, s.acct(t.Account.Acct)) - color.Set(color.Reset) - fmt.Fprint(w, " reblogged ") - color.Set(color.FgHiBlue) - fmt.Fprintln(w, s.acct(t.Reblog.Account.Acct)) - fmt.Fprintln(w, textContent(t.Reblog.Content)) - color.Set(color.Reset) - } else { - color.Set(color.FgHiRed) - fmt.Fprintln(w, s.acct(t.Account.Acct)) - color.Set(color.Reset) - fmt.Fprintln(w, textContent(t.Content)) - } -} - -func run() int { - app := makeApp() - - app.Before = func(c *cli.Context) error { - if c.Args().Get(0) == "init" { - file, err := configFile(c) - if err != nil { - return err - } - os.Remove(file) - } - - file, config, err := getConfig(c) - if err != nil { - return err - } - - client := mastodon.NewClient(config) - client.UserAgent = "mstdn" - app.Metadata = map[string]interface{}{ - "client": client, - "config": config, - "xsearch_url": "http://mastodonsearch.jp/cross/", - } - if config.AccessToken == "" { - return authenticate(client, config, file) - } - return nil - } - - fatalIf(app.Run(os.Args)) - return 0 -} - -func main() { - os.Exit(run()) -} diff --git a/cmd/mstdn/main_test.go b/cmd/mstdn/main_test.go deleted file mode 100644 index 19e6ab1..0000000 --- a/cmd/mstdn/main_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "io/ioutil" - "os" - "testing" - - "github.com/urfave/cli" -) - -func TestReadFileFile(t *testing.T) { - b, err := readFile("main.go") - if err != nil { - t.Fatal(err) - } - if len(b) == 0 { - t.Fatalf("should read something: %v", err) - } -} - -func TestReadFileStdin(t *testing.T) { - f, err := os.Open("main.go") - if err != nil { - t.Fatal(err) - } - defer f.Close() - stdin := os.Stdin - os.Stdin = f - defer func() { - os.Stdin = stdin - }() - - b, err := readFile("-") - if err != nil { - t.Fatal(err) - } - if len(b) == 0 { - t.Fatalf("should read something: %v", err) - } -} - -func TestTextContent(t *testing.T) { - tests := []struct { - input string - want string - }{ - {input: "", want: ""}, - {input: "

foo

", want: "foo"}, - {input: "

foo\nbar\nbaz

", want: "foobarbaz"}, - {input: "

foo\nbar
baz

", want: "foobar\nbaz"}, - } - for _, test := range tests { - got := textContent(test.input) - if got != test.want { - t.Fatalf("want %q but %q", test.want, got) - } - } -} - -func TestGetConfig(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "mstdn") - if err != nil { - t.Fatal(err) - } - home := os.Getenv("HOME") - appdata := os.Getenv("APPDATA") - os.Setenv("HOME", tmpdir) - os.Setenv("APPDATA", tmpdir) - defer func() { - os.RemoveAll(tmpdir) - os.Setenv("HOME", home) - os.Setenv("APPDATA", appdata) - }() - - app := makeApp() - set := flag.NewFlagSet("test", 0) - set.Parse([]string{"mstdn", "-profile", ""}) - c := cli.NewContext(app, set, nil) - file, config, err := getConfig(c) - if err != nil { - t.Fatal(err) - } - if _, err := os.Stat(file); err == nil { - t.Fatal("should not exists") - } - if config.AccessToken != "" { - t.Fatalf("should be empty: %v", config.AccessToken) - } - if config.ClientID == "" { - t.Fatalf("should not be empty") - } - if config.ClientSecret == "" { - t.Fatalf("should not be empty") - } - config.AccessToken = "foo" - b, err := json.MarshalIndent(config, "", " ") - if err != nil { - t.Fatal(err) - } - err = ioutil.WriteFile(file, b, 0700) - if err != nil { - t.Fatal(err) - } - file, config, err = getConfig(c) - if err != nil { - t.Fatal(err) - } - if _, err := os.Stat(file); err != nil { - t.Fatalf("should exists: %v", err) - } - if got := config.AccessToken; got != "foo" { - t.Fatalf("want %q but %q", "foo", got) - } -} - -func TestPrompt(t *testing.T) { - readUsername = func() (string, error) { - return "foo", nil - } - readPassword = func() (string, error) { - return "bar", nil - } - username, password, err := prompt() - if err != nil { - t.Fatal(err) - } - if username != "foo" { - t.Fatalf("want %q but %q", "foo", username) - } - if password != "bar" { - t.Fatalf("want %q but %q", "bar", password) - } -} diff --git a/go.mod b/go.mod index 5ab4e81..ab6f93c 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,3 @@ module github.com/mattn/go-mastodon go 1.16 - -require ( - github.com/gorilla/websocket v1.5.0 - github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 -) diff --git a/go.sum b/go.sum index 34589e8..e69de29 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +0,0 @@ -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/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= -github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= diff --git a/mastodon.go b/mastodon.go index c704e10..96fa129 100644 --- a/mastodon.go +++ b/mastodon.go @@ -12,8 +12,6 @@ import ( "path" "strings" "time" - - "github.com/tomnomnom/linkheader" ) // Config is a setting for access mastodon APIs. @@ -320,28 +318,28 @@ func newPagination(rawlink string) (*Pagination, error) { } p := &Pagination{} - for _, link := range linkheader.Parse(rawlink) { - switch link.Rel { - case "next": - maxID, err := getPaginationID(link.URL, "max_id") - if err != nil { - return nil, err - } - p.MaxID = maxID - case "prev": - sinceID, err := getPaginationID(link.URL, "since_id") - if err != nil { - return nil, err - } - p.SinceID = sinceID + // for _, link := range linkheader.Parse(rawlink) { + // switch link.Rel { + // case "next": + // maxID, err := getPaginationID(link.URL, "max_id") + // if err != nil { + // return nil, err + // } + // p.MaxID = maxID + // case "prev": + // sinceID, err := getPaginationID(link.URL, "since_id") + // if err != nil { + // return nil, err + // } + // p.SinceID = sinceID - minID, err := getPaginationID(link.URL, "min_id") - if err != nil { - return nil, err - } - p.MinID = minID - } - } + // minID, err := getPaginationID(link.URL, "min_id") + // if err != nil { + // return nil, err + // } + // p.MinID = minID + // } + // } return p, nil } diff --git a/streaming_ws.go b/streaming_ws.go deleted file mode 100644 index bd42bf9..0000000 --- a/streaming_ws.go +++ /dev/null @@ -1,195 +0,0 @@ -package mastodon - -import ( - "context" - "encoding/json" - "fmt" - "net/url" - "path" - "strings" - - "github.com/gorilla/websocket" -) - -// WSClient is a WebSocket client. -type WSClient struct { - websocket.Dialer - client *Client -} - -// NewWSClient return WebSocket client. -func (c *Client) NewWSClient() *WSClient { return &WSClient{client: c} } - -// Stream is a struct of data that flows in streaming. -type Stream struct { - Event string `json:"event"` - Payload interface{} `json:"payload"` -} - -// StreamingWSUser return channel to read events on home using WebSocket. -func (c *WSClient) StreamingWSUser(ctx context.Context) (chan Event, error) { - return c.streamingWS(ctx, "user", "") -} - -// StreamingWSPublic return channel to read events on public using WebSocket. -func (c *WSClient) StreamingWSPublic(ctx context.Context, isLocal bool) (chan Event, error) { - s := "public" - if isLocal { - s += ":local" - } - - return c.streamingWS(ctx, s, "") -} - -// StreamingWSHashtag return channel to read events on tagged timeline using WebSocket. -func (c *WSClient) StreamingWSHashtag(ctx context.Context, tag string, isLocal bool) (chan Event, error) { - s := "hashtag" - if isLocal { - s += ":local" - } - - return c.streamingWS(ctx, s, tag) -} - -// StreamingWSList return channel to read events on a list using WebSocket. -func (c *WSClient) StreamingWSList(ctx context.Context, id ID) (chan Event, error) { - return c.streamingWS(ctx, "list", string(id)) -} - -func (c *WSClient) streamingWS(ctx context.Context, stream, tag string) (chan Event, error) { - params := url.Values{} - params.Set("access_token", c.client.Config.AccessToken) - params.Set("stream", stream) - if tag != "" { - params.Set("tag", tag) - } - - u, err := changeWebSocketScheme(c.client.Config.Server) - if err != nil { - return nil, err - } - u.Path = path.Join(u.Path, "/api/v1/streaming") - u.RawQuery = params.Encode() - - q := make(chan Event) - go func() { - defer close(q) - for { - err := c.handleWS(ctx, u.String(), q) - if err != nil { - return - } - } - }() - - return q, nil -} - -func (c *WSClient) handleWS(ctx context.Context, rawurl string, q chan Event) error { - conn, err := c.dialRedirect(rawurl) - if err != nil { - q <- &ErrorEvent{err: err} - - // End. - return err - } - - // Close the WebSocket when the context is canceled. - go func() { - <-ctx.Done() - conn.Close() - }() - - for { - select { - case <-ctx.Done(): - q <- &ErrorEvent{err: ctx.Err()} - - // End. - return ctx.Err() - default: - } - - var s Stream - err := conn.ReadJSON(&s) - if err != nil { - q <- &ErrorEvent{err: err} - - // Reconnect. - break - } - - err = nil - switch s.Event { - case "update": - var status Status - err = json.Unmarshal([]byte(s.Payload.(string)), &status) - if err == nil { - q <- &UpdateEvent{Status: &status} - } - case "notification": - var notification Notification - err = json.Unmarshal([]byte(s.Payload.(string)), ¬ification) - if err == nil { - q <- &NotificationEvent{Notification: ¬ification} - } - case "delete": - if f, ok := s.Payload.(float64); ok { - q <- &DeleteEvent{ID: ID(fmt.Sprint(int64(f)))} - } else { - q <- &DeleteEvent{ID: ID(strings.TrimSpace(s.Payload.(string)))} - } - } - if err != nil { - q <- &ErrorEvent{err} - } - } - - return nil -} - -func (c *WSClient) dialRedirect(rawurl string) (conn *websocket.Conn, err error) { - for { - conn, rawurl, err = c.dial(rawurl) - if err != nil { - return nil, err - } else if conn != nil { - return conn, nil - } - } -} - -func (c *WSClient) dial(rawurl string) (*websocket.Conn, string, error) { - conn, resp, err := c.Dial(rawurl, nil) - if err != nil && err != websocket.ErrBadHandshake { - return nil, "", err - } - defer resp.Body.Close() - - if loc := resp.Header.Get("Location"); loc != "" { - u, err := changeWebSocketScheme(loc) - if err != nil { - return nil, "", err - } - - return nil, u.String(), nil - } - - return conn, "", err -} - -func changeWebSocketScheme(rawurl string) (*url.URL, error) { - u, err := url.Parse(rawurl) - if err != nil { - return nil, err - } - - switch u.Scheme { - case "http": - u.Scheme = "ws" - case "https": - u.Scheme = "wss" - } - - return u, nil -} diff --git a/streaming_ws_test.go b/streaming_ws_test.go deleted file mode 100644 index 856702a..0000000 --- a/streaming_ws_test.go +++ /dev/null @@ -1,281 +0,0 @@ -package mastodon - -import ( - "context" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/gorilla/websocket" -) - -func TestStreamingWSUser(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(wsMock)) - defer ts.Close() - - client := NewClient(&Config{Server: ts.URL}).NewWSClient() - ctx, cancel := context.WithCancel(context.Background()) - q, err := client.StreamingWSUser(ctx) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - - wsTest(t, q, cancel) -} - -func TestStreamingWSPublic(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(wsMock)) - defer ts.Close() - - client := NewClient(&Config{Server: ts.URL}).NewWSClient() - ctx, cancel := context.WithCancel(context.Background()) - q, err := client.StreamingWSPublic(ctx, false) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - - wsTest(t, q, cancel) -} - -func TestStreamingWSHashtag(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(wsMock)) - defer ts.Close() - - client := NewClient(&Config{Server: ts.URL}).NewWSClient() - ctx, cancel := context.WithCancel(context.Background()) - q, err := client.StreamingWSHashtag(ctx, "zzz", true) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - wsTest(t, q, cancel) - - ctx, cancel = context.WithCancel(context.Background()) - q, err = client.StreamingWSHashtag(ctx, "zzz", false) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - wsTest(t, q, cancel) -} - -func wsMock(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/api/v1/streaming" { - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - return - } - - u := websocket.Upgrader{} - conn, err := u.Upgrade(w, r, nil) - if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - defer conn.Close() - - err = conn.WriteMessage(websocket.TextMessage, - []byte(`{"event":"update","payload":"{\"content\":\"foo\"}"}`)) - if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - err = conn.WriteMessage(websocket.TextMessage, - []byte(`{"event":"notification","payload":"{\"id\":123}"}`)) - if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - err = conn.WriteMessage(websocket.TextMessage, - []byte(`{"event":"delete","payload":1234567}`)) - if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - err = conn.WriteMessage(websocket.TextMessage, - []byte(`{"event":"update","payload":""}`)) - if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - time.Sleep(10 * time.Second) -} - -func wsTest(t *testing.T, q chan Event, cancel func()) { - time.AfterFunc(time.Second, func() { - cancel() - }) - events := []Event{} - for e := range q { - events = append(events, e) - } - if len(events) != 6 { - t.Fatalf("result should be four: %d", len(events)) - } - if events[0].(*UpdateEvent).Status.Content != "foo" { - t.Fatalf("want %q but %q", "foo", events[0].(*UpdateEvent).Status.Content) - } - if events[1].(*NotificationEvent).Notification.ID != "123" { - t.Fatalf("want %q but %q", "123", events[1].(*NotificationEvent).Notification.ID) - } - if events[2].(*DeleteEvent).ID != "1234567" { - t.Fatalf("want %q but %q", "1234567", events[2].(*DeleteEvent).ID) - } - if errorEvent, ok := events[3].(*ErrorEvent); !ok { - t.Fatalf("should be fail: %v", errorEvent.err) - } - if errorEvent, ok := events[4].(*ErrorEvent); !ok { - t.Fatalf("should be fail: %v", errorEvent.err) - } - if errorEvent, ok := events[5].(*ErrorEvent); !ok { - t.Fatalf("should be fail: %v", errorEvent.err) - } -} - -func TestStreamingWS(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(wsMock)) - defer ts.Close() - - client := NewClient(&Config{Server: ":"}).NewWSClient() - _, err := client.StreamingWSPublic(context.Background(), true) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - - client = NewClient(&Config{Server: ts.URL}).NewWSClient() - ctx, cancel := context.WithCancel(context.Background()) - cancel() - q, err := client.StreamingWSPublic(ctx, true) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - go func() { - e := <-q - if errorEvent, ok := e.(*ErrorEvent); !ok { - t.Fatalf("should be fail: %v", errorEvent.err) - } - }() -} - -func TestHandleWS(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - u := websocket.Upgrader{} - conn, err := u.Upgrade(w, r, nil) - if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - defer conn.Close() - - err = conn.WriteMessage(websocket.TextMessage, - []byte(``)) - if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - time.Sleep(10 * time.Second) - })) - defer ts.Close() - - q := make(chan Event) - client := NewClient(&Config{}).NewWSClient() - - go func() { - e := <-q - if errorEvent, ok := e.(*ErrorEvent); !ok { - t.Fatalf("should be fail: %v", errorEvent.err) - } - }() - err := client.handleWS(context.Background(), ":", q) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - go func() { - e := <-q - if errorEvent, ok := e.(*ErrorEvent); !ok { - t.Fatalf("should be fail: %v", errorEvent.err) - } - }() - err = client.handleWS(ctx, "ws://"+ts.Listener.Addr().String(), q) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - - go func() { - e := <-q - if errorEvent, ok := e.(*ErrorEvent); !ok { - t.Fatalf("should be fail: %v", errorEvent.err) - } - }() - client.handleWS(context.Background(), "ws://"+ts.Listener.Addr().String(), q) -} - -func TestDialRedirect(t *testing.T) { - client := NewClient(&Config{}).NewWSClient() - _, err := client.dialRedirect(":") - if err == nil { - t.Fatalf("should be fail: %v", err) - } -} - -func TestDial(t *testing.T) { - canErr := true - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if canErr { - canErr = false - http.Redirect(w, r, ":", http.StatusMovedPermanently) - return - } - - http.Redirect(w, r, "http://www.example.com/", http.StatusMovedPermanently) - })) - defer ts.Close() - - client := NewClient(&Config{}).NewWSClient() - _, _, err := client.dial(":") - if err == nil { - t.Fatalf("should be fail: %v", err) - } - - _, _, err = client.dial("ws://" + ts.Listener.Addr().String()) - if err == nil { - t.Fatalf("should be fail: %v", err) - } - - _, rawurl, err := client.dial("ws://" + ts.Listener.Addr().String()) - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - if rawurl != "ws://www.example.com/" { - t.Fatalf("want %q but %q", "ws://www.example.com/", rawurl) - } -} - -func TestChangeWebSocketScheme(t *testing.T) { - _, err := changeWebSocketScheme(":") - if err == nil { - t.Fatalf("should be fail: %v", err) - } - - u, err := changeWebSocketScheme("http://example.com/") - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - if u.Scheme != "ws" { - t.Fatalf("want %q but %q", "ws", u.Scheme) - } - - u, err = changeWebSocketScheme("https://example.com/") - if err != nil { - t.Fatalf("should not be fail: %v", err) - } - if u.Scheme != "wss" { - t.Fatalf("want %q but %q", "wss", u.Scheme) - } -}