remove dependencies and client
This commit is contained in:
parent
5f0c9a21c2
commit
2542bf46ba
1
LICENSE
1
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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", `<http://example.com?since_id=890>; 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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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, `<div class="post"><div class="mst_content"><a href="http://example.com/@test/1"><p>三上</p></a></div></div>`)
|
||||
}
|
||||
},
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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, `<div class="post"><div class="mst_content"><a href="http://example.com/@test/1"><p>test status</p></a></div></div>`)
|
||||
},
|
||||
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, `<div class="post"><div class="mst_content"><a><p>test status</p></a></div></div>`)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, `<div class="post"><div class="mst_content"><a href="http://example.com/@test/1"><p>test status</p></a></div></div>`)
|
||||
}))
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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=
|
|
@ -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())
|
||||
}
|
|
@ -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: "<p>foo</p>", want: "foo"},
|
||||
{input: "<p>foo<span>\nbar\n</span>baz</p>", want: "foobarbaz"},
|
||||
{input: "<p>foo<span>\nbar<br></span>baz</p>", 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)
|
||||
}
|
||||
}
|
5
go.mod
5
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
|
||||
)
|
||||
|
|
4
go.sum
4
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=
|
44
mastodon.go
44
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
|
||||
}
|
||||
|
|
195
streaming_ws.go
195
streaming_ws.go
|
@ -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
|
||||
}
|
|
@ -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":"<html></html>"}`))
|
||||
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(`<html></html>`))
|
||||
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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user