diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index 351ae11..0000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-# These are supported funding model platforms
-
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: mattn # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
-tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
deleted file mode 100644
index 0d5395e..0000000
--- a/.github/workflows/test.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: test
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
-jobs:
- test:
- strategy:
- matrix:
- os: [windows-latest, macos-latest, ubuntu-latest]
- go: ["1.16", "1.17", "1.18", "1.19"]
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-go@v3
- with:
- go-version: ${{ matrix.go }}
-
- - run: go generate ./...
- - run: git diff --cached --exit-code
- - run: go test ./... -v -cover -coverprofile coverage.out
- - run: go test -bench . -benchmem
-
- - uses: codecov/codecov-action@v2
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..b65ca33
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+language: go
+go:
+ - tip
+before_install:
+ - go get github.com/mattn/goveralls
+ - go get golang.org/x/tools/cmd/cover
+script:
+ - $HOME/gopath/bin/goveralls -repotoken u2dqXvOxbIBr8eGxCjcgTkkN2JOSGx1fy
diff --git a/README.md b/README.md
index 8440744..a66ccb7 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,10 @@
# go-mastodon
-[](https://github.com/mattn/go-mastodon/actions?query=workflow%3Atest)
-[](https://codecov.io/gh/mattn/go-mastodon)
-[](https://pkg.go.dev/github.com/mattn/go-mastodon)
+[](https://travis-ci.org/mattn/go-mastodon)
+[](https://coveralls.io/github/mattn/go-mastodon?branch=master)
+[](http://godoc.org/github.com/mattn/go-mastodon)
[](https://goreportcard.com/report/github.com/mattn/go-mastodon)
-
## Usage
### Application
@@ -83,81 +82,43 @@ func main() {
* [x] GET /api/v1/accounts/:id/unblock
* [x] GET /api/v1/accounts/:id/mute
* [x] GET /api/v1/accounts/:id/unmute
-* [x] GET /api/v1/accounts/:id/lists
* [x] GET /api/v1/accounts/relationships
* [x] GET /api/v1/accounts/search
-* [x] GET /api/v1/apps/verify_credentials
-* [x] GET /api/v1/bookmarks
* [x] POST /api/v1/apps
* [x] GET /api/v1/blocks
-* [x] GET /api/v1/conversations
-* [x] DELETE /api/v1/conversations/:id
-* [x] POST /api/v1/conversations/:id/read
* [x] GET /api/v1/favourites
-* [x] GET /api/v1/filters
-* [x] POST /api/v1/filters
-* [x] GET /api/v1/filters/:id
-* [x] PUT /api/v1/filters/:id
-* [x] DELETE /api/v1/filters/:id
* [x] GET /api/v1/follow_requests
* [x] POST /api/v1/follow_requests/:id/authorize
* [x] POST /api/v1/follow_requests/:id/reject
* [x] POST /api/v1/follows
* [x] GET /api/v1/instance
-* [x] GET /api/v1/instance/activity
-* [x] GET /api/v1/instance/peers
-* [x] GET /api/v1/lists
-* [x] GET /api/v1/lists/:id/accounts
-* [x] GET /api/v1/lists/:id
-* [x] POST /api/v1/lists
-* [x] PUT /api/v1/lists/:id
-* [x] DELETE /api/v1/lists/:id
-* [x] POST /api/v1/lists/:id/accounts
-* [x] DELETE /api/v1/lists/:id/accounts
* [x] POST /api/v1/media
* [x] GET /api/v1/mutes
* [x] GET /api/v1/notifications
* [x] GET /api/v1/notifications/:id
-* [x] POST /api/v1/notifications/:id/dismiss
* [x] POST /api/v1/notifications/clear
-* [x] POST /api/v1/push/subscription
-* [x] GET /api/v1/push/subscription
-* [x] PUT /api/v1/push/subscription
-* [x] DELETE /api/v1/push/subscription
* [x] GET /api/v1/reports
* [x] POST /api/v1/reports
-* [x] GET /api/v2/search
+* [x] GET /api/v1/search
* [x] GET /api/v1/statuses/:id
* [x] GET /api/v1/statuses/:id/context
* [x] GET /api/v1/statuses/:id/card
-* [x] GET /api/v1/statuses/:id/history
* [x] GET /api/v1/statuses/:id/reblogged_by
-* [x] GET /api/v1/statuses/:id/source
* [x] GET /api/v1/statuses/:id/favourited_by
* [x] POST /api/v1/statuses
-* [x] PUT /api/v1/statuses/:id
* [x] DELETE /api/v1/statuses/:id
* [x] POST /api/v1/statuses/:id/reblog
* [x] POST /api/v1/statuses/:id/unreblog
* [x] POST /api/v1/statuses/:id/favourite
* [x] POST /api/v1/statuses/:id/unfavourite
-* [x] POST /api/v1/statuses/:id/bookmark
-* [x] POST /api/v1/statuses/:id/unbookmark
* [x] GET /api/v1/timelines/home
* [x] GET /api/v1/timelines/public
* [x] GET /api/v1/timelines/tag/:hashtag
-* [x] GET /api/v1/timelines/list/:id
-* [x] GET /api/v1/streaming/user
-* [x] GET /api/v1/streaming/public
-* [x] GET /api/v1/streaming/hashtag?tag=:hashtag
-* [x] GET /api/v1/streaming/hashtag/local?tag=:hashtag
-* [x] GET /api/v1/streaming/list?list=:list_id
-* [x] GET /api/v1/streaming/direct
## Installation
-```shell
-go install github.com/mattn/go-mastodon@latest
+```
+$ go get github.com/mattn/go-mastodon
```
## License
diff --git a/accounts.go b/accounts.go
index 0b43e4c..1e9abb3 100644
--- a/accounts.go
+++ b/accounts.go
@@ -5,49 +5,26 @@ import (
"fmt"
"net/http"
"net/url"
- "strconv"
"time"
)
-// Account holds information for a mastodon account.
+// Account hold information for mastodon account.
type Account struct {
- ID ID `json:"id"`
- Username string `json:"username"`
- Acct string `json:"acct"`
- DisplayName string `json:"display_name"`
- Locked bool `json:"locked"`
- CreatedAt time.Time `json:"created_at"`
- FollowersCount int64 `json:"followers_count"`
- FollowingCount int64 `json:"following_count"`
- StatusesCount int64 `json:"statuses_count"`
- Note string `json:"note"`
- URL string `json:"url"`
- Avatar string `json:"avatar"`
- AvatarStatic string `json:"avatar_static"`
- Header string `json:"header"`
- HeaderStatic string `json:"header_static"`
- Emojis []Emoji `json:"emojis"`
- Moved *Account `json:"moved"`
- Fields []Field `json:"fields"`
- Bot bool `json:"bot"`
- Discoverable bool `json:"discoverable"`
- Source *AccountSource `json:"source"`
-}
-
-// Field is a Mastodon account profile field.
-type Field struct {
- Name string `json:"name"`
- Value string `json:"value"`
- VerifiedAt time.Time `json:"verified_at"`
-}
-
-// AccountSource is a Mastodon account profile field.
-type AccountSource struct {
- Privacy *string `json:"privacy"`
- Sensitive *bool `json:"sensitive"`
- Language *string `json:"language"`
- Note *string `json:"note"`
- Fields *[]Field `json:"fields"`
+ ID ID `json:"id"`
+ Username string `json:"username"`
+ Acct string `json:"acct"`
+ DisplayName string `json:"display_name"`
+ Locked bool `json:"locked"`
+ CreatedAt time.Time `json:"created_at"`
+ FollowersCount int64 `json:"followers_count"`
+ FollowingCount int64 `json:"following_count"`
+ StatusesCount int64 `json:"statuses_count"`
+ Note string `json:"note"`
+ URL string `json:"url"`
+ Avatar string `json:"avatar"`
+ AvatarStatic string `json:"avatar_static"`
+ Header string `json:"header"`
+ HeaderStatic string `json:"header_static"`
}
// GetAccount return Account.
@@ -60,7 +37,7 @@ func (c *Client) GetAccount(ctx context.Context, id ID) (*Account, error) {
return &account, nil
}
-// GetAccountCurrentUser returns the Account of current user.
+// GetAccountCurrentUser return Account of current user.
func (c *Client) GetAccountCurrentUser(ctx context.Context) (*Account, error) {
var account Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/verify_credentials", nil, &account, nil)
@@ -76,9 +53,6 @@ type Profile struct {
// If it is empty, update it with empty.
DisplayName *string
Note *string
- Locked *bool
- Fields *[]Field
- Source *AccountSource
// Set the base64 encoded character string of the image.
Avatar string
@@ -94,26 +68,6 @@ func (c *Client) AccountUpdate(ctx context.Context, profile *Profile) (*Account,
if profile.Note != nil {
params.Set("note", *profile.Note)
}
- if profile.Locked != nil {
- params.Set("locked", strconv.FormatBool(*profile.Locked))
- }
- if profile.Fields != nil {
- for idx, field := range *profile.Fields {
- params.Set(fmt.Sprintf("fields_attributes[%d][name]", idx), field.Name)
- params.Set(fmt.Sprintf("fields_attributes[%d][value]", idx), field.Value)
- }
- }
- if profile.Source != nil {
- if profile.Source.Privacy != nil {
- params.Set("source[privacy]", *profile.Source.Privacy)
- }
- if profile.Source.Sensitive != nil {
- params.Set("source[sensitive]", strconv.FormatBool(*profile.Source.Sensitive))
- }
- if profile.Source.Language != nil {
- params.Set("source[language]", *profile.Source.Language)
- }
- }
if profile.Avatar != "" {
params.Set("avatar", profile.Avatar)
}
@@ -129,7 +83,7 @@ func (c *Client) AccountUpdate(ctx context.Context, profile *Profile) (*Account,
return &account, nil
}
-// GetAccountStatuses return statuses by specified account.
+// GetAccountStatuses return statuses by specified accuont.
func (c *Client) GetAccountStatuses(ctx context.Context, id ID, pg *Pagination) ([]*Status, error) {
var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(string(id))), nil, &statuses, pg)
@@ -139,19 +93,7 @@ func (c *Client) GetAccountStatuses(ctx context.Context, id ID, pg *Pagination)
return statuses, nil
}
-// GetAccountPinnedStatuses returns statuses pinned by specified accuont.
-func (c *Client) GetAccountPinnedStatuses(ctx context.Context, id ID) ([]*Status, error) {
- var statuses []*Status
- params := url.Values{}
- params.Set("pinned", "true")
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(string(id))), params, &statuses, nil)
- if err != nil {
- return nil, err
- }
- return statuses, nil
-}
-
-// GetAccountFollowers returns followers list.
+// GetAccountFollowers return followers list.
func (c *Client) GetAccountFollowers(ctx context.Context, id ID, pg *Pagination) ([]*Account, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/followers", url.PathEscape(string(id))), nil, &accounts, pg)
@@ -161,7 +103,7 @@ func (c *Client) GetAccountFollowers(ctx context.Context, id ID, pg *Pagination)
return accounts, nil
}
-// GetAccountFollowing returns following list.
+// GetAccountFollowing return following list.
func (c *Client) GetAccountFollowing(ctx context.Context, id ID, pg *Pagination) ([]*Account, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/following", url.PathEscape(string(id))), nil, &accounts, pg)
@@ -171,7 +113,7 @@ func (c *Client) GetAccountFollowing(ctx context.Context, id ID, pg *Pagination)
return accounts, nil
}
-// GetBlocks returns block list.
+// GetBlocks return block list.
func (c *Client) GetBlocks(ctx context.Context, pg *Pagination) ([]*Account, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts, pg)
@@ -181,21 +123,17 @@ func (c *Client) GetBlocks(ctx context.Context, pg *Pagination) ([]*Account, err
return accounts, nil
}
-// Relationship holds information for relationship to the account.
+// Relationship hold information for relation-ship to the account.
type Relationship struct {
- ID ID `json:"id"`
- Following bool `json:"following"`
- FollowedBy bool `json:"followed_by"`
- Blocking bool `json:"blocking"`
- Muting bool `json:"muting"`
- MutingNotifications bool `json:"muting_notifications"`
- Requested bool `json:"requested"`
- DomainBlocking bool `json:"domain_blocking"`
- ShowingReblogs bool `json:"showing_reblogs"`
- Endorsed bool `json:"endorsed"`
+ ID ID `json:"id"`
+ Following bool `json:"following"`
+ FollowedBy bool `json:"followed_by"`
+ Blocking bool `json:"blocking"`
+ Muting bool `json:"muting"`
+ Requested bool `json:"requested"`
}
-// AccountFollow follows the account.
+// AccountFollow follow the account.
func (c *Client) AccountFollow(ctx context.Context, id ID) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/follow", url.PathEscape(string(id))), nil, &relationship, nil)
@@ -205,7 +143,7 @@ func (c *Client) AccountFollow(ctx context.Context, id ID) (*Relationship, error
return &relationship, nil
}
-// AccountUnfollow unfollows the account.
+// AccountUnfollow unfollow the account.
func (c *Client) AccountUnfollow(ctx context.Context, id ID) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unfollow", url.PathEscape(string(id))), nil, &relationship, nil)
@@ -215,7 +153,7 @@ func (c *Client) AccountUnfollow(ctx context.Context, id ID) (*Relationship, err
return &relationship, nil
}
-// AccountBlock blocks the account.
+// AccountBlock block the account.
func (c *Client) AccountBlock(ctx context.Context, id ID) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/block", url.PathEscape(string(id))), nil, &relationship, nil)
@@ -225,7 +163,7 @@ func (c *Client) AccountBlock(ctx context.Context, id ID) (*Relationship, error)
return &relationship, nil
}
-// AccountUnblock unblocks the account.
+// AccountUnblock unblock the account.
func (c *Client) AccountUnblock(ctx context.Context, id ID) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unblock", url.PathEscape(string(id))), nil, &relationship, nil)
@@ -235,7 +173,7 @@ func (c *Client) AccountUnblock(ctx context.Context, id ID) (*Relationship, erro
return &relationship, nil
}
-// AccountMute mutes the account.
+// AccountMute mute the account.
func (c *Client) AccountMute(ctx context.Context, id ID) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/mute", url.PathEscape(string(id))), nil, &relationship, nil)
@@ -245,7 +183,7 @@ func (c *Client) AccountMute(ctx context.Context, id ID) (*Relationship, error)
return &relationship, nil
}
-// AccountUnmute unmutes the account.
+// AccountUnmute unmute the account.
func (c *Client) AccountUnmute(ctx context.Context, id ID) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unmute", url.PathEscape(string(id))), nil, &relationship, nil)
@@ -255,7 +193,7 @@ func (c *Client) AccountUnmute(ctx context.Context, id ID) (*Relationship, error
return &relationship, nil
}
-// GetAccountRelationships returns relationship for the account.
+// GetAccountRelationships return relationship for the account.
func (c *Client) GetAccountRelationships(ctx context.Context, ids []string) ([]*Relationship, error) {
params := url.Values{}
for _, id := range ids {
@@ -270,7 +208,7 @@ func (c *Client) GetAccountRelationships(ctx context.Context, ids []string) ([]*
return relationships, nil
}
-// AccountsSearch searches accounts by query.
+// AccountsSearch search accounts by query.
func (c *Client) AccountsSearch(ctx context.Context, q string, limit int64) ([]*Account, error) {
params := url.Values{}
params.Set("q", q)
@@ -284,7 +222,7 @@ func (c *Client) AccountsSearch(ctx context.Context, q string, limit int64) ([]*
return accounts, nil
}
-// FollowRemoteUser sends follow-request.
+// FollowRemoteUser send follow-request.
func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, error) {
params := url.Values{}
params.Set("uri", uri)
@@ -297,7 +235,7 @@ func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, er
return &account, nil
}
-// GetFollowRequests returns follow requests.
+// GetFollowRequests return follow-requests.
func (c *Client) GetFollowRequests(ctx context.Context, pg *Pagination) ([]*Account, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts, pg)
@@ -307,12 +245,12 @@ func (c *Client) GetFollowRequests(ctx context.Context, pg *Pagination) ([]*Acco
return accounts, nil
}
-// FollowRequestAuthorize authorizes the follow request of user with id.
+// FollowRequestAuthorize is authorize the follow request of user with id.
func (c *Client) FollowRequestAuthorize(ctx context.Context, id ID) error {
return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/authorize", url.PathEscape(string(id))), nil, nil, nil)
}
-// FollowRequestReject rejects the follow request of user with id.
+// FollowRequestReject is rejects the follow request of user with id.
func (c *Client) FollowRequestReject(ctx context.Context, id ID) error {
return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/reject", url.PathEscape(string(id))), nil, nil, nil)
}
diff --git a/accounts_test.go b/accounts_test.go
index 47e0310..7603cc6 100644
--- a/accounts_test.go
+++ b/accounts_test.go
@@ -6,7 +6,6 @@ import (
"net/http"
"net/http/httptest"
"testing"
- "time"
)
func TestGetAccount(t *testing.T) {
@@ -16,6 +15,7 @@ func TestGetAccount(t *testing.T) {
return
}
fmt.Fprintln(w, `{"username": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -47,6 +47,7 @@ func TestGetAccountCurrentUser(t *testing.T) {
return
}
fmt.Fprintln(w, `{"username": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -78,6 +79,7 @@ func TestAccountUpdate(t *testing.T) {
return
}
fmt.Fprintln(w, `{"username": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -91,15 +93,9 @@ func TestAccountUpdate(t *testing.T) {
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- tbool := true
- fields := []Field{{"foo", "bar", time.Time{}}, {"dum", "baz", time.Time{}}}
- source := AccountSource{Language: String("de"), Privacy: String("public"), Sensitive: &tbool}
a, err := client.AccountUpdate(context.Background(), &Profile{
DisplayName: String("display_name"),
Note: String("note"),
- Locked: &tbool,
- Fields: &fields,
- Source: &source,
Avatar: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADrCAYAAAA...",
Header: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADrCAYAAAA...",
})
@@ -118,6 +114,7 @@ func TestGetAccountStatuses(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"content": "foo"}, {"content": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -143,43 +140,6 @@ func TestGetAccountStatuses(t *testing.T) {
}
}
-func TestGetAccountPinnedStatuses(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/accounts/1234567/statuses" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- pinned := r.URL.Query().Get("pinned")
- if pinned != "true" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `[{"content": "foo"}, {"content": "bar"}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetAccountPinnedStatuses(context.Background(), "123")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- ss, err := client.GetAccountPinnedStatuses(context.Background(), "1234567")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if ss[0].Content != "foo" {
- t.Fatalf("want %q but %q", "foo", ss[0].Content)
- }
- if ss[1].Content != "bar" {
- t.Fatalf("want %q but %q", "bar", ss[1].Content)
- }
-}
-
func TestGetAccountFollowers(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/v1/accounts/1234567/followers" {
@@ -187,6 +147,7 @@ func TestGetAccountFollowers(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -222,6 +183,7 @@ func TestGetAccountFollowing(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -259,6 +221,7 @@ func TestGetBlocks(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -294,6 +257,7 @@ func TestAccountFollow(t *testing.T) {
return
}
fmt.Fprintln(w, `{"id":1234567,"following":true}`)
+ return
}))
defer ts.Close()
@@ -303,11 +267,11 @@ func TestAccountFollow(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.AccountFollow(context.Background(), "123")
+ rel, err := client.AccountFollow(context.Background(), "123")
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- rel, err := client.AccountFollow(context.Background(), "1234567")
+ rel, err = client.AccountFollow(context.Background(), "1234567")
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -326,6 +290,7 @@ func TestAccountUnfollow(t *testing.T) {
return
}
fmt.Fprintln(w, `{"id":1234567,"following":false}`)
+ return
}))
defer ts.Close()
@@ -335,11 +300,11 @@ func TestAccountUnfollow(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.AccountUnfollow(context.Background(), "123")
+ rel, err := client.AccountUnfollow(context.Background(), "123")
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- rel, err := client.AccountUnfollow(context.Background(), "1234567")
+ rel, err = client.AccountUnfollow(context.Background(), "1234567")
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -358,6 +323,7 @@ func TestAccountBlock(t *testing.T) {
return
}
fmt.Fprintln(w, `{"id":1234567,"blocking":true}`)
+ return
}))
defer ts.Close()
@@ -390,6 +356,7 @@ func TestAccountUnblock(t *testing.T) {
return
}
fmt.Fprintln(w, `{"id":1234567,"blocking":false}`)
+ return
}))
defer ts.Close()
@@ -422,6 +389,7 @@ func TestAccountMute(t *testing.T) {
return
}
fmt.Fprintln(w, `{"id":1234567,"muting":true}`)
+ return
}))
defer ts.Close()
@@ -454,6 +422,7 @@ func TestAccountUnmute(t *testing.T) {
return
}
fmt.Fprintln(w, `{"id":1234567,"muting":false}`)
+ return
}))
defer ts.Close()
@@ -519,6 +488,7 @@ func TestAccountsSearch(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"username": "foobar"}, {"username": "barfoo"}]`)
+ return
}))
defer ts.Close()
@@ -554,6 +524,7 @@ func TestFollowRemoteUser(t *testing.T) {
return
}
fmt.Fprintln(w, `{"username": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -585,6 +556,7 @@ func TestGetFollowRequests(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -670,6 +642,7 @@ func TestGetMutes(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`)
+ return
}))
defer ts.Close()
diff --git a/apps.go b/apps.go
index c885ec9..6d7552f 100644
--- a/apps.go
+++ b/apps.go
@@ -18,24 +18,19 @@ type AppConfig struct {
// Where the user should be redirected after authorization (for no redirect, use urn:ietf:wg:oauth:2.0:oob)
RedirectURIs string
- // This can be a space-separated list of items listed on the /settings/applications/new page of any Mastodon
- // instance. "read", "write", and "follow" are top-level scopes that include all the permissions of the more
- // specific scopes like "read:favourites", "write:statuses", and "write:follows".
+ // This can be a space-separated list of the following items: "read", "write" and "follow".
Scopes string
// Optional.
Website string
}
-// Application is a mastodon application.
+// Application is mastodon application.
type Application struct {
- ID ID `json:"id"`
+ ID int64 `json:"id"`
RedirectURI string `json:"redirect_uri"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
-
- // AuthURI is not part of the Mastodon API; it is generated by go-mastodon.
- AuthURI string `json:"auth_uri,omitempty"`
}
// RegisterApp returns the mastodon application.
@@ -78,36 +73,5 @@ func RegisterApp(ctx context.Context, appConfig *AppConfig) (*Application, error
return nil, err
}
- u, err = url.Parse(appConfig.Server)
- if err != nil {
- return nil, err
- }
- u.Path = path.Join(u.Path, "/oauth/authorize")
- u.RawQuery = url.Values{
- "scope": {appConfig.Scopes},
- "response_type": {"code"},
- "redirect_uri": {app.RedirectURI},
- "client_id": {app.ClientID},
- }.Encode()
-
- app.AuthURI = u.String()
-
return &app, nil
}
-
-// ApplicationVerification is mastodon application.
-type ApplicationVerification struct {
- Name string `json:"name"`
- Website string `json:"website"`
- VapidKey string `json:"vapid_key"`
-}
-
-// VerifyAppCredentials returns the mastodon application.
-func (c *Client) VerifyAppCredentials(ctx context.Context) (*ApplicationVerification, error) {
- var application ApplicationVerification
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/apps/verify_credentials", nil, &application, nil)
- if err != nil {
- return nil, err
- }
- return &application, nil
-}
diff --git a/apps_test.go b/apps_test.go
index b8403e8..bab20bf 100644
--- a/apps_test.go
+++ b/apps_test.go
@@ -26,7 +26,8 @@ func TestRegisterApp(t *testing.T) {
fmt.Fprintln(w, `
Apps`)
return
}
- fmt.Fprintln(w, `{"id": 123, "client_id": "foo", "client_secret": "bar"}`)
+ fmt.Fprintln(w, `{"client_id": "foo", "client_secret": "bar"}`)
+ return
}))
defer ts.Close()
@@ -63,9 +64,6 @@ func TestRegisterApp(t *testing.T) {
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- if string(app.ID) != "123" {
- t.Fatalf("want %q but %q", "bar", app.ClientSecret)
- }
if app.ClientID != "foo" {
t.Fatalf("want %q but %q", "foo", app.ClientID)
}
@@ -78,6 +76,7 @@ func TestRegisterAppWithCancel(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
fmt.Fprintln(w, `{"client_id": "foo", "client_secret": "bar"}`)
+ return
}))
defer ts.Close()
@@ -90,53 +89,7 @@ func TestRegisterAppWithCancel(t *testing.T) {
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- if want := fmt.Sprintf("Post %q: context canceled", ts.URL+"/api/v1/apps"); want != err.Error() {
+ if want := "Post " + ts.URL + "/api/v1/apps: context canceled"; want != err.Error() {
t.Fatalf("want %q but %q", want, err.Error())
}
}
-
-func TestVerifyAppCredentials(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Header.Get("Authorization") != "Bearer zoo" {
- http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
- return
- }
- if r.URL.Path != "/api/v1/apps/verify_credentials" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `{"name":"zzz","website":"yyy","vapid_key":"xxx"}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zip",
- })
- _, err := client.VerifyAppCredentials(context.Background())
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
-
- client = NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- a, err := client.VerifyAppCredentials(context.Background())
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if a.Name != "zzz" {
- t.Fatalf("want %q but %q", "zzz", a.Name)
- }
- if a.Website != "yyy" {
- t.Fatalf("want %q but %q", "yyy", a.Name)
- }
- if a.VapidKey != "xxx" {
- t.Fatalf("want %q but %q", "xxx", a.Name)
- }
-}
diff --git a/cmd/mstdn/cmd_account.go b/cmd/mstdn/cmd_account.go
index 6501769..23083c2 100644
--- a/cmd/mstdn/cmd_account.go
+++ b/cmd/mstdn/cmd_account.go
@@ -5,7 +5,7 @@ import (
"fmt"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdAccount(c *cli.Context) error {
diff --git a/cmd/mstdn/cmd_account_test.go b/cmd/mstdn/cmd_account_test.go
index d35a088..8894451 100644
--- a/cmd/mstdn/cmd_account_test.go
+++ b/cmd/mstdn/cmd_account_test.go
@@ -6,7 +6,7 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdAccount(t *testing.T) {
diff --git a/cmd/mstdn/cmd_delete.go b/cmd/mstdn/cmd_delete.go
index acc8a36..83755c5 100644
--- a/cmd/mstdn/cmd_delete.go
+++ b/cmd/mstdn/cmd_delete.go
@@ -3,9 +3,10 @@ package main
import (
"context"
"errors"
+ "strconv"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdDelete(c *cli.Context) error {
@@ -14,7 +15,11 @@ func cmdDelete(c *cli.Context) error {
return errors.New("arguments required")
}
for i := 0; i < c.NArg(); i++ {
- err := client.DeleteStatus(context.Background(), mastodon.ID(c.Args().Get(i)))
+ id, err := strconv.ParseInt(c.Args().Get(i), 10, 64)
+ if err != nil {
+ return err
+ }
+ err = client.DeleteStatus(context.Background(), id)
if err != nil {
return err
}
diff --git a/cmd/mstdn/cmd_delete_test.go b/cmd/mstdn/cmd_delete_test.go
index 4e202ca..e37442d 100644
--- a/cmd/mstdn/cmd_delete_test.go
+++ b/cmd/mstdn/cmd_delete_test.go
@@ -5,7 +5,7 @@ import (
"net/http"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdDelete(t *testing.T) {
diff --git a/cmd/mstdn/cmd_follow.go b/cmd/mstdn/cmd_follow.go
index ca87963..e064f03 100644
--- a/cmd/mstdn/cmd_follow.go
+++ b/cmd/mstdn/cmd_follow.go
@@ -5,7 +5,7 @@ import (
"errors"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdFollow(c *cli.Context) error {
diff --git a/cmd/mstdn/cmd_follow_test.go b/cmd/mstdn/cmd_follow_test.go
index ce2cfce..e4f43c9 100644
--- a/cmd/mstdn/cmd_follow_test.go
+++ b/cmd/mstdn/cmd_follow_test.go
@@ -5,7 +5,7 @@ import (
"net/http"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdFollow(t *testing.T) {
diff --git a/cmd/mstdn/cmd_followers.go b/cmd/mstdn/cmd_followers.go
index 9d9f10f..ecb4a79 100644
--- a/cmd/mstdn/cmd_followers.go
+++ b/cmd/mstdn/cmd_followers.go
@@ -6,7 +6,7 @@ import (
"time"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdFollowers(c *cli.Context) error {
@@ -25,11 +25,9 @@ func cmdFollowers(c *cli.Context) error {
return err
}
followers = append(followers, fs...)
- if pg.MaxID == "" {
+ if pg.MaxID == 0 {
break
}
- pg.SinceID = ""
- pg.MinID = ""
time.Sleep(10 * time.Second)
}
s := newScreen(config)
diff --git a/cmd/mstdn/cmd_followers_test.go b/cmd/mstdn/cmd_followers_test.go
index 17b7f4c..f8c5080 100644
--- a/cmd/mstdn/cmd_followers_test.go
+++ b/cmd/mstdn/cmd_followers_test.go
@@ -6,7 +6,7 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdFollowers(t *testing.T) {
diff --git a/cmd/mstdn/cmd_instance.go b/cmd/mstdn/cmd_instance.go
index 84042dd..b287efc 100644
--- a/cmd/mstdn/cmd_instance.go
+++ b/cmd/mstdn/cmd_instance.go
@@ -3,10 +3,9 @@ package main
import (
"context"
"fmt"
- "sort"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdInstance(c *cli.Context) error {
@@ -19,26 +18,5 @@ func cmdInstance(c *cli.Context) error {
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 fa133c3..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/v2"
-)
-
-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 86d8915..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/v2"
-)
-
-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
index a8435ad..f5a6279 100644
--- a/cmd/mstdn/cmd_instance_test.go
+++ b/cmd/mstdn/cmd_instance_test.go
@@ -6,7 +6,7 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdInstance(t *testing.T) {
diff --git a/cmd/mstdn/cmd_mikami.go b/cmd/mstdn/cmd_mikami.go
index 069dd44..abbfc18 100644
--- a/cmd/mstdn/cmd_mikami.go
+++ b/cmd/mstdn/cmd_mikami.go
@@ -1,7 +1,7 @@
package main
import (
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdMikami(c *cli.Context) error {
diff --git a/cmd/mstdn/cmd_mikami_test.go b/cmd/mstdn/cmd_mikami_test.go
index 3d61bf7..3fe9eae 100644
--- a/cmd/mstdn/cmd_mikami_test.go
+++ b/cmd/mstdn/cmd_mikami_test.go
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdMikami(t *testing.T) {
diff --git a/cmd/mstdn/cmd_notification.go b/cmd/mstdn/cmd_notification.go
index 177494f..b32ba4e 100644
--- a/cmd/mstdn/cmd_notification.go
+++ b/cmd/mstdn/cmd_notification.go
@@ -6,7 +6,7 @@ import (
"github.com/fatih/color"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdNotification(c *cli.Context) error {
diff --git a/cmd/mstdn/cmd_notification_test.go b/cmd/mstdn/cmd_notification_test.go
index aac67f3..25a9429 100644
--- a/cmd/mstdn/cmd_notification_test.go
+++ b/cmd/mstdn/cmd_notification_test.go
@@ -6,7 +6,7 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdNotification(t *testing.T) {
diff --git a/cmd/mstdn/cmd_search.go b/cmd/mstdn/cmd_search.go
index c6bdd87..6d7e81b 100644
--- a/cmd/mstdn/cmd_search.go
+++ b/cmd/mstdn/cmd_search.go
@@ -6,7 +6,7 @@ import (
"fmt"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdSearch(c *cli.Context) error {
diff --git a/cmd/mstdn/cmd_search_test.go b/cmd/mstdn/cmd_search_test.go
index fd5e740..2d00b93 100644
--- a/cmd/mstdn/cmd_search_test.go
+++ b/cmd/mstdn/cmd_search_test.go
@@ -6,15 +6,15 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "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": "わろす"}]}`)
+ case "/api/v1/search":
+ fmt.Fprintln(w, `{"accounts": [{"id": 234, "acct": "zzz"}], "statuses":[{"id": 345, "content": "yyy"}], "hashtags": ["www", "わろす"]}`)
return
}
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
diff --git a/cmd/mstdn/cmd_stream.go b/cmd/mstdn/cmd_stream.go
index 03995a3..258a658 100644
--- a/cmd/mstdn/cmd_stream.go
+++ b/cmd/mstdn/cmd_stream.go
@@ -10,7 +10,7 @@ import (
"text/template"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
// SimpleJSON is a struct for output JSON for data to be simple used
@@ -44,7 +44,9 @@ func cmdStream(c *cli.Context) error {
"nl": func(s string) string {
return s + "\n"
},
- "text": textContent,
+ "text": func(s string) string {
+ return textContent(s)
+ },
}).Parse(asFormat)
if err != nil {
return err
@@ -87,16 +89,7 @@ func cmdStream(c *cli.Context) error {
if asJSON {
json.NewEncoder(c.App.Writer).Encode(e)
} else if asSimpleJSON {
- switch t := e.(type) {
- case *mastodon.UpdateEvent:
- 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),
- })
- case *mastodon.UpdateEditEvent:
+ if t, ok := e.(*mastodon.UpdateEvent); ok {
json.NewEncoder(c.App.Writer).Encode(&SimpleJSON{
ID: t.Status.ID,
Username: t.Status.Account.Username,
@@ -111,8 +104,6 @@ func cmdStream(c *cli.Context) error {
switch t := e.(type) {
case *mastodon.UpdateEvent:
s.displayStatus(c.App.Writer, t.Status)
- case *mastodon.UpdateEditEvent:
- s.displayStatus(c.App.Writer, t.Status)
case *mastodon.NotificationEvent:
// TODO s.displayStatus(c.App.Writer, t.Notification.Status)
case *mastodon.ErrorEvent:
diff --git a/cmd/mstdn/cmd_stream_test.go b/cmd/mstdn/cmd_stream_test.go
index b3b37c7..92c339b 100644
--- a/cmd/mstdn/cmd_stream_test.go
+++ b/cmd/mstdn/cmd_stream_test.go
@@ -14,7 +14,8 @@ import (
)
func TestCmdStream(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ var ts *httptest.Server
+ 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
diff --git a/cmd/mstdn/cmd_test.go b/cmd/mstdn/cmd_test.go
index ecb841f..14043c3 100644
--- a/cmd/mstdn/cmd_test.go
+++ b/cmd/mstdn/cmd_test.go
@@ -6,7 +6,7 @@ import (
"net/http/httptest"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func testWithServer(h http.HandlerFunc, testFuncs ...func(*cli.App)) string {
diff --git a/cmd/mstdn/cmd_timeline.go b/cmd/mstdn/cmd_timeline.go
index 6272af4..d9a0024 100644
--- a/cmd/mstdn/cmd_timeline.go
+++ b/cmd/mstdn/cmd_timeline.go
@@ -2,11 +2,9 @@ package main
import (
"context"
- "errors"
- "strings"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdTimeline(c *cli.Context) error {
@@ -22,69 +20,3 @@ func cmdTimeline(c *cli.Context) error {
}
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
-}
-
-func cmdTimelineHashtag(c *cli.Context) error {
- if !c.Args().Present() {
- return errors.New("arguments required")
- }
- local := c.Bool("local")
- tag := strings.TrimLeft(argstr(c), "#")
-
- client := c.App.Metadata["client"].(*mastodon.Client)
- config := c.App.Metadata["config"].(*mastodon.Config)
- timeline, err := client.GetTimelineHashtag(context.Background(), tag, local, 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
index e24ba14..b0801a8 100644
--- a/cmd/mstdn/cmd_timeline_test.go
+++ b/cmd/mstdn/cmd_timeline_test.go
@@ -6,7 +6,7 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdTimeline(t *testing.T) {
@@ -14,13 +14,7 @@ func TestCmdTimeline(t *testing.T) {
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"}}]`)
+ fmt.Fprintln(w, `[{"content": "zzz"}]`)
return
}
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
@@ -28,25 +22,9 @@ func TestCmdTimeline(t *testing.T) {
},
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)
+ if !strings.Contains(out, "zzz") {
+ t.Fatalf("%q should be contained in output of command: %v", "zzz", out)
}
}
diff --git a/cmd/mstdn/cmd_toot.go b/cmd/mstdn/cmd_toot.go
index b777a65..29b6f4c 100644
--- a/cmd/mstdn/cmd_toot.go
+++ b/cmd/mstdn/cmd_toot.go
@@ -3,10 +3,9 @@ package main
import (
"context"
"errors"
- "fmt"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdToot(c *cli.Context) error {
@@ -27,7 +26,7 @@ func cmdToot(c *cli.Context) error {
client := c.App.Metadata["client"].(*mastodon.Client)
_, err := client.PostStatus(context.Background(), &mastodon.Toot{
Status: toot,
- InReplyToID: mastodon.ID(fmt.Sprint(c.String("i"))),
+ InReplyToID: c.Int64("i"),
})
return err
}
diff --git a/cmd/mstdn/cmd_toot_test.go b/cmd/mstdn/cmd_toot_test.go
index bc083be..4f997f0 100644
--- a/cmd/mstdn/cmd_toot_test.go
+++ b/cmd/mstdn/cmd_toot_test.go
@@ -5,7 +5,7 @@ import (
"net/http"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdToot(t *testing.T) {
diff --git a/cmd/mstdn/cmd_upload.go b/cmd/mstdn/cmd_upload.go
index 68d8b70..15f912e 100644
--- a/cmd/mstdn/cmd_upload.go
+++ b/cmd/mstdn/cmd_upload.go
@@ -6,7 +6,7 @@ import (
"fmt"
"github.com/mattn/go-mastodon"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdUpload(c *cli.Context) error {
diff --git a/cmd/mstdn/cmd_upload_test.go b/cmd/mstdn/cmd_upload_test.go
index 0e76bd1..585d943 100644
--- a/cmd/mstdn/cmd_upload_test.go
+++ b/cmd/mstdn/cmd_upload_test.go
@@ -6,7 +6,7 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdUpload(t *testing.T) {
diff --git a/cmd/mstdn/cmd_xsearch.go b/cmd/mstdn/cmd_xsearch.go
index 223d9fa..2c7950b 100644
--- a/cmd/mstdn/cmd_xsearch.go
+++ b/cmd/mstdn/cmd_xsearch.go
@@ -6,7 +6,7 @@ import (
"net/url"
"github.com/PuerkitoBio/goquery"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func cmdXSearch(c *cli.Context) error {
diff --git a/cmd/mstdn/cmd_xsearch_test.go b/cmd/mstdn/cmd_xsearch_test.go
index 43c2baa..f3dc105 100644
--- a/cmd/mstdn/cmd_xsearch_test.go
+++ b/cmd/mstdn/cmd_xsearch_test.go
@@ -8,7 +8,7 @@ import (
"strings"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestCmdXSearch(t *testing.T) {
diff --git a/cmd/mstdn/go.mod b/cmd/mstdn/go.mod
deleted file mode 100644
index 19d310d..0000000
--- a/cmd/mstdn/go.mod
+++ /dev/null
@@ -1,15 +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.13.0
- github.com/urfave/cli/v2 v2.23.5 // indirect
- 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 65758c5..0000000
--- a/cmd/mstdn/go.sum
+++ /dev/null
@@ -1,53 +0,0 @@
-github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-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.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 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
-github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-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/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/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.13.0 h1:kkpCmfxnnnWIie2rCljcvaVrNYmsFq1ynTJH5kn1Ip4=
-github.com/urfave/cli v1.13.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli/v2 v2.23.5 h1:xbrU7tAYviSpqeR3X4nEFWUdB/uDZ6DE+HxmRU7Xtyw=
-github.com/urfave/cli/v2 v2.23.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
-github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
-github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
-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-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.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/cmd/mstdn/main.go b/cmd/mstdn/main.go
index a52cf10..392f748 100644
--- a/cmd/mstdn/main.go
+++ b/cmd/mstdn/main.go
@@ -17,7 +17,7 @@ import (
"github.com/fatih/color"
"github.com/mattn/go-mastodon"
"github.com/mattn/go-tty"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
"golang.org/x/net/html"
)
@@ -183,26 +183,26 @@ func makeApp() *cli.App {
app.Usage = "mastodon client"
app.Version = "0.0.1"
app.Flags = []cli.Flag{
- &cli.StringFlag{
+ cli.StringFlag{
Name: "profile",
Usage: "profile name",
Value: "",
},
}
- app.Commands = []*cli.Command{
+ app.Commands = []cli.Command{
{
Name: "toot",
Usage: "post toot",
Flags: []cli.Flag{
- &cli.StringFlag{
+ cli.StringFlag{
Name: "ff",
Usage: "post utf-8 string from a file(\"-\" means STDIN)",
Value: "",
},
- &cli.StringFlag{
+ cli.IntFlag{
Name: "i",
Usage: "in-reply-to",
- Value: "",
+ Value: 0,
},
},
Action: cmdToot,
@@ -211,19 +211,19 @@ func makeApp() *cli.App {
Name: "stream",
Usage: "stream statuses",
Flags: []cli.Flag{
- &cli.StringFlag{
+ cli.StringFlag{
Name: "type",
Usage: "stream type (public,public/local,user:NAME,hashtag:TAG)",
},
- &cli.BoolFlag{
+ cli.BoolFlag{
Name: "json",
Usage: "output JSON",
},
- &cli.BoolFlag{
+ cli.BoolFlag{
Name: "simplejson",
Usage: "output simple JSON",
},
- &cli.StringFlag{
+ cli.StringFlag{
Name: "template",
Usage: "output with tamplate format",
},
@@ -235,37 +235,6 @@ func makeApp() *cli.App {
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: "timeline-tag",
- Flags: []cli.Flag{
- cli.BoolFlag{
- Name: "local",
- Usage: "local tags only",
- },
- },
- Usage: "show tagged timeline",
- Action: cmdTimelineHashtag,
- },
{
Name: "notification",
Usage: "show notification",
@@ -276,16 +245,6 @@ func makeApp() *cli.App {
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",
@@ -401,7 +360,6 @@ func run() int {
}
client := mastodon.NewClient(config)
- client.UserAgent = "mstdn"
app.Metadata = map[string]interface{}{
"client": client,
"config": config,
diff --git a/cmd/mstdn/main_test.go b/cmd/mstdn/main_test.go
index 123ab88..19e6ab1 100644
--- a/cmd/mstdn/main_test.go
+++ b/cmd/mstdn/main_test.go
@@ -7,7 +7,7 @@ import (
"os"
"testing"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli"
)
func TestReadFileFile(t *testing.T) {
diff --git a/compat.go b/compat.go
index 789906d..0031ae4 100644
--- a/compat.go
+++ b/compat.go
@@ -3,7 +3,6 @@ package mastodon
import (
"encoding/json"
"fmt"
- "strconv"
)
type ID string
@@ -24,26 +23,3 @@ func (id *ID) UnmarshalJSON(data []byte) error {
*id = ID(fmt.Sprint(n))
return nil
}
-
-type Sbool bool
-
-func (s *Sbool) UnmarshalJSON(data []byte) error {
- if len(data) > 0 && data[0] == '"' && data[len(data)-1] == '"' {
- var str string
- if err := json.Unmarshal(data, &str); err != nil {
- return err
- }
- b, err := strconv.ParseBool(str)
- if err != nil {
- return err
- }
- *s = Sbool(b)
- return nil
- }
- var b bool
- if err := json.Unmarshal(data, &b); err != nil {
- return err
- }
- *s = Sbool(b)
- return nil
-}
diff --git a/example_test.go b/example_test.go
index a25a3ca..18716a2 100644
--- a/example_test.go
+++ b/example_test.go
@@ -56,7 +56,7 @@ func ExamplePagination() {
log.Fatal(err)
}
followers = append(followers, fs...)
- if pg.MaxID == "" {
+ if pg.MaxID == 0 {
break
}
time.Sleep(10 * time.Second)
diff --git a/filters.go b/filters.go
deleted file mode 100644
index 56f1229..0000000
--- a/filters.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package mastodon
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "time"
-)
-
-// Filter is metadata for a filter of users.
-type Filter struct {
- ID ID `json:"id"`
- Phrase string `json:"phrase"`
- Context []string `json:"context"`
- WholeWord bool `json:"whole_word"`
- ExpiresAt time.Time `json:"expires_at"`
- Irreversible bool `json:"irreversible"`
-}
-
-// GetFilters returns all the filters on the current account.
-func (c *Client) GetFilters(ctx context.Context) ([]*Filter, error) {
- var filters []*Filter
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/filters", nil, &filters, nil)
- if err != nil {
- return nil, err
- }
- return filters, nil
-}
-
-// GetFilter retrieves a filter by ID.
-func (c *Client) GetFilter(ctx context.Context, id ID) (*Filter, error) {
- var filter Filter
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(string(id))), nil, &filter, nil)
- if err != nil {
- return nil, err
- }
- return &filter, nil
-}
-
-// CreateFilter creates a new filter.
-func (c *Client) CreateFilter(ctx context.Context, filter *Filter) (*Filter, error) {
- if filter == nil {
- return nil, errors.New("filter can't be nil")
- }
- if filter.Phrase == "" {
- return nil, errors.New("phrase can't be empty")
- }
- if len(filter.Context) == 0 {
- return nil, errors.New("context can't be empty")
- }
- params := url.Values{}
- params.Set("phrase", filter.Phrase)
- for _, c := range filter.Context {
- params.Add("context[]", c)
- }
- if filter.WholeWord {
- params.Add("whole_word", "true")
- }
- if filter.Irreversible {
- params.Add("irreversible", "true")
- }
- if !filter.ExpiresAt.IsZero() {
- diff := time.Until(filter.ExpiresAt)
- params.Add("expires_in", fmt.Sprintf("%.0f", diff.Seconds()))
- }
-
- var f Filter
- err := c.doAPI(ctx, http.MethodPost, "/api/v1/filters", params, &f, nil)
- if err != nil {
- return nil, err
- }
- return &f, nil
-}
-
-// UpdateFilter updates a filter.
-func (c *Client) UpdateFilter(ctx context.Context, id ID, filter *Filter) (*Filter, error) {
- if filter == nil {
- return nil, errors.New("filter can't be nil")
- }
- if id == ID("") {
- return nil, errors.New("ID can't be empty")
- }
- if filter.Phrase == "" {
- return nil, errors.New("phrase can't be empty")
- }
- if len(filter.Context) == 0 {
- return nil, errors.New("context can't be empty")
- }
- params := url.Values{}
- params.Set("phrase", filter.Phrase)
- for _, c := range filter.Context {
- params.Add("context[]", c)
- }
- if filter.WholeWord {
- params.Add("whole_word", "true")
- } else {
- params.Add("whole_word", "false")
- }
- if filter.Irreversible {
- params.Add("irreversible", "true")
- } else {
- params.Add("irreversible", "false")
- }
- if !filter.ExpiresAt.IsZero() {
- diff := time.Until(filter.ExpiresAt)
- params.Add("expires_in", fmt.Sprintf("%.0f", diff.Seconds()))
- } else {
- params.Add("expires_in", "")
- }
-
- var f Filter
- err := c.doAPI(ctx, http.MethodPut, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(string(id))), params, &f, nil)
- if err != nil {
- return nil, err
- }
- return &f, nil
-}
-
-// DeleteFilter removes a filter.
-func (c *Client) DeleteFilter(ctx context.Context, id ID) error {
- return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(string(id))), nil, nil, nil)
-}
diff --git a/filters_test.go b/filters_test.go
deleted file mode 100644
index 71e440d..0000000
--- a/filters_test.go
+++ /dev/null
@@ -1,342 +0,0 @@
-package mastodon
-
-import (
- "context"
- "fmt"
- "net/http"
- "net/http/httptest"
- "sort"
- "strings"
- "testing"
- "time"
-)
-
-func TestGetFilters(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintln(w, `[{"id": "6191", "phrase": "rust", "context": ["home"], "whole_word": true, "expires_at": "2019-05-21T13:47:31.333Z", "irreversible": false}, {"id": "5580", "phrase": "@twitter.com", "context": ["home", "notifications", "public", "thread"], "whole_word": false, "expires_at": null, "irreversible": true}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- d, err := time.Parse(time.RFC3339Nano, "2019-05-21T13:47:31.333Z")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- tf := []Filter{
- {
- ID: ID("6191"),
- Phrase: "rust",
- Context: []string{"home"},
- WholeWord: true,
- ExpiresAt: d,
- Irreversible: false,
- },
- {
- ID: ID("5580"),
- Phrase: "@twitter.com",
- Context: []string{"notifications", "home", "thread", "public"},
- WholeWord: false,
- ExpiresAt: time.Time{},
- Irreversible: true,
- },
- }
-
- filters, err := client.GetFilters(context.Background())
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(filters) != 2 {
- t.Fatalf("result should be two: %d", len(filters))
- }
- for i, f := range tf {
- if filters[i].ID != f.ID {
- t.Fatalf("want %q but %q", string(f.ID), filters[i].ID)
- }
- if filters[i].Phrase != f.Phrase {
- t.Fatalf("want %q but %q", f.Phrase, filters[i].Phrase)
- }
- sort.Strings(filters[i].Context)
- sort.Strings(f.Context)
- if strings.Join(filters[i].Context, ", ") != strings.Join(f.Context, ", ") {
- t.Fatalf("want %q but %q", f.Context, filters[i].Context)
- }
- if filters[i].ExpiresAt != f.ExpiresAt {
- t.Fatalf("want %q but %q", f.ExpiresAt, filters[i].ExpiresAt)
- }
- if filters[i].WholeWord != f.WholeWord {
- t.Fatalf("want %t but %t", f.WholeWord, filters[i].WholeWord)
- }
- if filters[i].Irreversible != f.Irreversible {
- t.Fatalf("want %t but %t", f.Irreversible, filters[i].Irreversible)
- }
- }
-}
-
-func TestGetFilter(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/filters/1" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `{"id": "1", "phrase": "rust", "context": ["home"], "whole_word": true, "expires_at": "2019-05-21T13:47:31.333Z", "irreversible": false}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetFilter(context.Background(), "2")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- d, err := time.Parse(time.RFC3339Nano, "2019-05-21T13:47:31.333Z")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- tf := Filter{
- ID: ID("1"),
- Phrase: "rust",
- Context: []string{"home"},
- WholeWord: true,
- ExpiresAt: d,
- Irreversible: false,
- }
- filter, err := client.GetFilter(context.Background(), "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if filter.ID != tf.ID {
- t.Fatalf("want %q but %q", string(tf.ID), filter.ID)
- }
- if filter.Phrase != tf.Phrase {
- t.Fatalf("want %q but %q", tf.Phrase, filter.Phrase)
- }
- sort.Strings(filter.Context)
- sort.Strings(tf.Context)
- if strings.Join(filter.Context, ", ") != strings.Join(tf.Context, ", ") {
- t.Fatalf("want %q but %q", tf.Context, filter.Context)
- }
- if filter.ExpiresAt != tf.ExpiresAt {
- t.Fatalf("want %q but %q", tf.ExpiresAt, filter.ExpiresAt)
- }
- if filter.WholeWord != tf.WholeWord {
- t.Fatalf("want %t but %t", tf.WholeWord, filter.WholeWord)
- }
- if filter.Irreversible != tf.Irreversible {
- t.Fatalf("want %t but %t", tf.Irreversible, filter.Irreversible)
- }
-}
-
-func TestCreateFilter(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.PostFormValue("phrase") != "rust" && r.PostFormValue("phrase") != "@twitter.com" {
- http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
- return
- }
- if r.PostFormValue("phrase") == "rust" {
- fmt.Fprintln(w, `{"id": "1", "phrase": "rust", "context": ["home"], "whole_word": true, "expires_at": "2019-05-21T13:47:31.333Z", "irreversible": true}`)
- return
- } else {
- fmt.Fprintln(w, `{"id": "2", "phrase": "@twitter.com", "context": ["home", "notifications", "public", "thread"], "whole_word": false, "expires_at": null, "irreversible": false}`)
- return
- }
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.CreateFilter(context.Background(), nil)
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- _, err = client.CreateFilter(context.Background(), &Filter{Context: []string{"home"}})
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- _, err = client.CreateFilter(context.Background(), &Filter{Phrase: "rust"})
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- _, err = client.CreateFilter(context.Background(), &Filter{Phrase: "Test", Context: []string{"home"}})
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
-
- d, err := time.Parse(time.RFC3339Nano, "2019-05-21T13:47:31.333Z")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- tf := []Filter{
- {
- ID: ID("1"),
- Phrase: "rust",
- Context: []string{"home"},
- WholeWord: true,
- ExpiresAt: d,
- Irreversible: true,
- },
- {
- ID: ID("2"),
- Phrase: "@twitter.com",
- Context: []string{"notifications", "home", "thread", "public"},
- WholeWord: false,
- ExpiresAt: time.Time{},
- Irreversible: false,
- },
- }
- for _, f := range tf {
- filter, err := client.CreateFilter(context.Background(), &f)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if filter.ID != f.ID {
- t.Fatalf("want %q but %q", string(f.ID), filter.ID)
- }
- if filter.Phrase != f.Phrase {
- t.Fatalf("want %q but %q", f.Phrase, filter.Phrase)
- }
- sort.Strings(filter.Context)
- sort.Strings(f.Context)
- if strings.Join(filter.Context, ", ") != strings.Join(f.Context, ", ") {
- t.Fatalf("want %q but %q", f.Context, filter.Context)
- }
- if filter.ExpiresAt != f.ExpiresAt {
- t.Fatalf("want %q but %q", f.ExpiresAt, filter.ExpiresAt)
- }
- if filter.WholeWord != f.WholeWord {
- t.Fatalf("want %t but %t", f.WholeWord, filter.WholeWord)
- }
- if filter.Irreversible != f.Irreversible {
- t.Fatalf("want %t but %t", f.Irreversible, filter.Irreversible)
- }
- }
-}
-
-func TestUpdateFilter(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path == "/api/v1/filters/1" {
- fmt.Fprintln(w, `{"id": "1", "phrase": "rust", "context": ["home"], "whole_word": true, "expires_at": "2019-05-21T13:47:31.333Z", "irreversible": true}`)
- return
- } else if r.URL.Path == "/api/v1/filters/2" {
- fmt.Fprintln(w, `{"id": "2", "phrase": "@twitter.com", "context": ["home", "notifications", "public", "thread"], "whole_word": false, "expires_at": null, "irreversible": false}`)
- return
- } else {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.UpdateFilter(context.Background(), ID("1"), nil)
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- _, err = client.UpdateFilter(context.Background(), ID(""), &Filter{Phrase: ""})
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- _, err = client.UpdateFilter(context.Background(), ID("2"), &Filter{Phrase: ""})
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- _, err = client.UpdateFilter(context.Background(), ID("2"), &Filter{Phrase: "rust"})
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- _, err = client.UpdateFilter(context.Background(), ID("3"), &Filter{Phrase: "rust", Context: []string{"home"}})
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
-
- d, err := time.Parse(time.RFC3339Nano, "2019-05-21T13:47:31.333Z")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- tf := []Filter{
- {
- ID: ID("1"),
- Phrase: "rust",
- Context: []string{"home"},
- WholeWord: true,
- ExpiresAt: d,
- Irreversible: true,
- },
- {
- ID: ID("2"),
- Phrase: "@twitter.com",
- Context: []string{"notifications", "home", "thread", "public"},
- WholeWord: false,
- ExpiresAt: time.Time{},
- Irreversible: false,
- },
- }
- for _, f := range tf {
- filter, err := client.UpdateFilter(context.Background(), f.ID, &f)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if filter.ID != f.ID {
- t.Fatalf("want %q but %q", string(f.ID), filter.ID)
- }
- if filter.Phrase != f.Phrase {
- t.Fatalf("want %q but %q", f.Phrase, filter.Phrase)
- }
- sort.Strings(filter.Context)
- sort.Strings(f.Context)
- if strings.Join(filter.Context, ", ") != strings.Join(f.Context, ", ") {
- t.Fatalf("want %q but %q", f.Context, filter.Context)
- }
- if filter.ExpiresAt != f.ExpiresAt {
- t.Fatalf("want %q but %q", f.ExpiresAt, filter.ExpiresAt)
- }
- if filter.WholeWord != f.WholeWord {
- t.Fatalf("want %t but %t", f.WholeWord, filter.WholeWord)
- }
- if filter.Irreversible != f.Irreversible {
- t.Fatalf("want %t but %t", f.Irreversible, filter.Irreversible)
- }
- }
-}
-
-func TestDeleteFilter(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/filters/1" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- err := client.DeleteFilter(context.Background(), "2")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- err = client.DeleteFilter(context.Background(), "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-}
diff --git a/go.mod b/go.mod
deleted file mode 100644
index 5ab4e81..0000000
--- a/go.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-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
deleted file mode 100644
index 34589e8..0000000
--- a/go.sum
+++ /dev/null
@@ -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/go.test.sh b/go.test.sh
deleted file mode 100755
index a7deaca..0000000
--- a/go.test.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-echo "" > coverage.txt
-
-for d in $(go list ./... | grep -v vendor); do
- go test -coverprofile=profile.out -covermode=atomic "$d"
- if [ -f profile.out ]; then
- cat profile.out >> coverage.txt
- rm profile.out
- fi
-done
diff --git a/helper_test.go b/helper_test.go
index 0d4b2ce..7da80b9 100644
--- a/helper_test.go
+++ b/helper_test.go
@@ -12,13 +12,13 @@ const wantBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHEAAABxCAYAAA
func TestBase64EncodeFileName(t *testing.T) {
// Error in os.Open.
- _, err := Base64EncodeFileName("fail")
+ uri, err := Base64EncodeFileName("fail")
if err == nil {
t.Fatalf("should be fail: %v", err)
}
// Success.
- uri, err := Base64EncodeFileName("testdata/logo.png")
+ uri, err = Base64EncodeFileName("testdata/logo.png")
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -29,7 +29,7 @@ func TestBase64EncodeFileName(t *testing.T) {
func TestBase64Encode(t *testing.T) {
// Error in file.Stat.
- _, err := Base64Encode(nil)
+ uri, err := Base64Encode(nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
@@ -43,7 +43,7 @@ func TestBase64Encode(t *testing.T) {
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- _, err = Base64Encode(logo)
+ uri, err = Base64Encode(logo)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
@@ -53,7 +53,7 @@ func TestBase64Encode(t *testing.T) {
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- uri, err := Base64Encode(logo)
+ uri, err = Base64Encode(logo)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
diff --git a/instance.go b/instance.go
index a2aa7f4..9b45c25 100644
--- a/instance.go
+++ b/instance.go
@@ -5,39 +5,15 @@ import (
"net/http"
)
-// Instance holds information for a mastodon instance.
+// Instance hold information for mastodon instance.
type Instance struct {
- URI string `json:"uri"`
- Title string `json:"title"`
- Description string `json:"description"`
- EMail string `json:"email"`
- Version string `json:"version,omitempty"`
- Thumbnail string `json:"thumbnail,omitempty"`
- URLs map[string]string `json:"urls,omitempty"`
- Stats *InstanceStats `json:"stats,omitempty"`
- Languages []string `json:"languages"`
- ContactAccount *Account `json:"contact_account"`
- Configuration *InstanceConfig `json:"configuration"`
+ URI string `json:"uri"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ EMail string `json:"email"`
}
-type InstanceConfigMap map[string]int
-
-// InstanceConfig holds configuration accessible for clients.
-type InstanceConfig struct {
- Accounts *InstanceConfigMap `json:"accounts"`
- Statuses *InstanceConfigMap `json:"statuses"`
- MediaAttachments map[string]interface{} `json:"media_attachments"`
- Polls *InstanceConfigMap `json:"polls"`
-}
-
-// InstanceStats holds information for mastodon instance stats.
-type InstanceStats struct {
- UserCount int64 `json:"user_count"`
- StatusCount int64 `json:"status_count"`
- DomainCount int64 `json:"domain_count"`
-}
-
-// GetInstance returns Instance.
+// GetInstance return Instance.
func (c *Client) GetInstance(ctx context.Context) (*Instance, error) {
var instance Instance
err := c.doAPI(ctx, http.MethodGet, "/api/v1/instance", nil, &instance, nil)
@@ -46,36 +22,3 @@ func (c *Client) GetInstance(ctx context.Context) (*Instance, error) {
}
return &instance, nil
}
-
-// GetConfig returns InstanceConfig.
-func (c *Instance) GetConfig() *InstanceConfig {
- return c.Configuration
-}
-
-// WeeklyActivity holds information for mastodon weekly activity.
-type WeeklyActivity struct {
- Week Unixtime `json:"week"`
- Statuses int64 `json:"statuses,string"`
- Logins int64 `json:"logins,string"`
- Registrations int64 `json:"registrations,string"`
-}
-
-// GetInstanceActivity returns instance activity.
-func (c *Client) GetInstanceActivity(ctx context.Context) ([]*WeeklyActivity, error) {
- var activity []*WeeklyActivity
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/instance/activity", nil, &activity, nil)
- if err != nil {
- return nil, err
- }
- return activity, nil
-}
-
-// GetInstancePeers returns instance peers.
-func (c *Client) GetInstancePeers(ctx context.Context) ([]string, error) {
- var peers []string
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/instance/peers", nil, &peers, nil)
- if err != nil {
- return nil, err
- }
- return peers, nil
-}
diff --git a/instance_test.go b/instance_test.go
index fb73c0d..6724b53 100644
--- a/instance_test.go
+++ b/instance_test.go
@@ -6,7 +6,6 @@ import (
"net/http"
"net/http/httptest"
"testing"
- "time"
)
func TestGetInstance(t *testing.T) {
@@ -17,7 +16,7 @@ func TestGetInstance(t *testing.T) {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- fmt.Fprintln(w, `{"title": "mastodon", "uri": "http://mstdn.example.com", "description": "test mastodon", "email": "mstdn@mstdn.example.com", "contact_account": {"username": "mattn"}}`)
+ fmt.Fprintln(w, `{"title": "mastodon"}`)
}))
defer ts.Close()
@@ -38,151 +37,4 @@ func TestGetInstance(t *testing.T) {
if ins.Title != "mastodon" {
t.Fatalf("want %q but %q", "mastodon", ins.Title)
}
- if ins.URI != "http://mstdn.example.com" {
- t.Fatalf("want %q but %q", "http://mstdn.example.com", ins.URI)
- }
- if ins.Description != "test mastodon" {
- t.Fatalf("want %q but %q", "test mastodon", ins.Description)
- }
- if ins.EMail != "mstdn@mstdn.example.com" {
- t.Fatalf("want %q but %q", "mstdn@mstdn.example.com", ins.EMail)
- }
- if ins.ContactAccount.Username != "mattn" {
- t.Fatalf("want %q but %q", "mattn", ins.ContactAccount.Username)
- }
-}
-
-func TestGetInstanceMore(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), http.StatusInternalServerError)
- return
- }
- fmt.Fprintln(w, `{"title": "mastodon", "uri": "http://mstdn.example.com", "description": "test mastodon", "email": "mstdn@mstdn.example.com", "version": "0.0.1", "urls":{"foo":"http://stream1.example.com", "bar": "http://stream2.example.com"}, "thumbnail": "http://mstdn.example.com/logo.png", "configuration":{"accounts": {"max_featured_tags": 10}, "statuses": {"max_characters": 500}}, "stats":{"user_count":1, "status_count":2, "domain_count":3}}}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetInstance(context.Background())
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- ins, err := client.GetInstance(context.Background())
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if ins.Title != "mastodon" {
- t.Fatalf("want %q but %q", "mastodon", ins.Title)
- }
- if ins.URI != "http://mstdn.example.com" {
- t.Fatalf("want %q but %q", "mastodon", ins.URI)
- }
- if ins.Description != "test mastodon" {
- t.Fatalf("want %q but %q", "test mastodon", ins.Description)
- }
- if ins.EMail != "mstdn@mstdn.example.com" {
- t.Fatalf("want %q but %q", "mstdn@mstdn.example.com", ins.EMail)
- }
- if ins.Version != "0.0.1" {
- t.Fatalf("want %q but %q", "0.0.1", ins.Version)
- }
- if ins.URLs["foo"] != "http://stream1.example.com" {
- t.Fatalf("want %q but %q", "http://stream1.example.com", ins.Version)
- }
- if ins.URLs["bar"] != "http://stream2.example.com" {
- t.Fatalf("want %q but %q", "http://stream2.example.com", ins.Version)
- }
- if ins.Thumbnail != "http://mstdn.example.com/logo.png" {
- t.Fatalf("want %q but %q", "http://mstdn.example.com/logo.png", ins.Thumbnail)
- }
- if ins.Stats == nil {
- t.Fatal("stats should not be nil")
- }
- if ins.Stats.UserCount != 1 {
- t.Fatalf("want %v but %v", 1, ins.Stats.UserCount)
- }
- if ins.Stats.StatusCount != 2 {
- t.Fatalf("want %v but %v", 2, ins.Stats.StatusCount)
- }
- if ins.Stats.DomainCount != 3 {
- t.Fatalf("want %v but %v", 3, ins.Stats.DomainCount)
- }
-
- cfg := ins.GetConfig()
- if cfg.Accounts == nil {
- t.Error("expected accounts to be non nil")
- }
- if cfg.Statuses == nil {
- t.Error("expected statuses to be non nil")
- }
-
-}
-
-func TestGetInstanceActivity(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), http.StatusInternalServerError)
- return
- }
- fmt.Fprintln(w, `[{"week":"1516579200","statuses":"1","logins":"1","registrations":"0"}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- })
- _, err := client.GetInstanceActivity(context.Background())
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- activity, err := client.GetInstanceActivity(context.Background())
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if activity[0].Week != Unixtime(time.Unix(1516579200, 0)) {
- t.Fatalf("want %v but %v", Unixtime(time.Unix(1516579200, 0)), activity[0].Week)
- }
- if activity[0].Logins != 1 {
- t.Fatalf("want %q but %q", 1, activity[0].Logins)
- }
-}
-
-func TestGetInstancePeers(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), http.StatusInternalServerError)
- return
- }
- fmt.Fprintln(w, `["mastodon.social","mstdn.jp"]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- })
- _, err := client.GetInstancePeers(context.Background())
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- peers, err := client.GetInstancePeers(context.Background())
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if peers[0] != "mastodon.social" {
- t.Fatalf("want %q but %q", "mastodon.social", peers[0])
- }
- if peers[1] != "mstdn.jp" {
- t.Fatalf("want %q but %q", "mstdn.jp", peers[1])
- }
}
diff --git a/lists.go b/lists.go
deleted file mode 100644
index 59610cc..0000000
--- a/lists.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package mastodon
-
-import (
- "context"
- "fmt"
- "net/http"
- "net/url"
-)
-
-// List is metadata for a list of users.
-type List struct {
- ID ID `json:"id"`
- Title string `json:"title"`
-}
-
-// GetLists returns all the lists on the current account.
-func (c *Client) GetLists(ctx context.Context) ([]*List, error) {
- var lists []*List
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/lists", nil, &lists, nil)
- if err != nil {
- return nil, err
- }
- return lists, nil
-}
-
-// GetAccountLists returns the lists containing a given account.
-func (c *Client) GetAccountLists(ctx context.Context, id ID) ([]*List, error) {
- var lists []*List
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/lists", url.PathEscape(string(id))), nil, &lists, nil)
- if err != nil {
- return nil, err
- }
- return lists, nil
-}
-
-// GetListAccounts returns the accounts in a given list.
-func (c *Client) GetListAccounts(ctx context.Context, id ID) ([]*Account, error) {
- var accounts []*Account
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(string(id))), url.Values{"limit": {"0"}}, &accounts, nil)
- if err != nil {
- return nil, err
- }
- return accounts, nil
-}
-
-// GetList retrieves a list by ID.
-func (c *Client) GetList(ctx context.Context, id ID) (*List, error) {
- var list List
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/lists/%s", url.PathEscape(string(id))), nil, &list, nil)
- if err != nil {
- return nil, err
- }
- return &list, nil
-}
-
-// CreateList creates a new list with a given title.
-func (c *Client) CreateList(ctx context.Context, title string) (*List, error) {
- params := url.Values{}
- params.Set("title", title)
-
- var list List
- err := c.doAPI(ctx, http.MethodPost, "/api/v1/lists", params, &list, nil)
- if err != nil {
- return nil, err
- }
- return &list, nil
-}
-
-// RenameList assigns a new title to a list.
-func (c *Client) RenameList(ctx context.Context, id ID, title string) (*List, error) {
- params := url.Values{}
- params.Set("title", title)
-
- var list List
- err := c.doAPI(ctx, http.MethodPut, fmt.Sprintf("/api/v1/lists/%s", url.PathEscape(string(id))), params, &list, nil)
- if err != nil {
- return nil, err
- }
- return &list, nil
-}
-
-// DeleteList removes a list.
-func (c *Client) DeleteList(ctx context.Context, id ID) error {
- return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/lists/%s", url.PathEscape(string(id))), nil, nil, nil)
-}
-
-// AddToList adds accounts to a list.
-//
-// Only accounts already followed by the user can be added to a list.
-func (c *Client) AddToList(ctx context.Context, list ID, accounts ...ID) error {
- params := url.Values{}
- for _, acct := range accounts {
- params.Add("account_ids", string(acct))
- }
-
- return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(string(list))), params, nil, nil)
-}
-
-// RemoveFromList removes accounts from a list.
-func (c *Client) RemoveFromList(ctx context.Context, list ID, accounts ...ID) error {
- params := url.Values{}
- for _, acct := range accounts {
- params.Add("account_ids", string(acct))
- }
-
- return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(string(list))), params, nil, nil)
-}
diff --git a/lists_test.go b/lists_test.go
deleted file mode 100644
index b1c9e3b..0000000
--- a/lists_test.go
+++ /dev/null
@@ -1,280 +0,0 @@
-package mastodon
-
-import (
- "context"
- "fmt"
- "net/http"
- "net/http/httptest"
- "testing"
-)
-
-func TestGetLists(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/lists" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `[{"id": "1", "title": "foo"}, {"id": "2", "title": "bar"}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- lists, err := client.GetLists(context.Background())
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(lists) != 2 {
- t.Fatalf("result should be two: %d", len(lists))
- }
- if lists[0].Title != "foo" {
- t.Fatalf("want %q but %q", "foo", lists[0].Title)
- }
- if lists[1].Title != "bar" {
- t.Fatalf("want %q but %q", "bar", lists[1].Title)
- }
-}
-
-func TestGetAccountLists(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/accounts/1/lists" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `[{"id": "1", "title": "foo"}, {"id": "2", "title": "bar"}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetAccountLists(context.Background(), "2")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- lists, err := client.GetAccountLists(context.Background(), "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(lists) != 2 {
- t.Fatalf("result should be two: %d", len(lists))
- }
- if lists[0].Title != "foo" {
- t.Fatalf("want %q but %q", "foo", lists[0].Title)
- }
- if lists[1].Title != "bar" {
- t.Fatalf("want %q but %q", "bar", lists[1].Title)
- }
-}
-
-func TestGetListAccounts(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/lists/1/accounts" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetListAccounts(context.Background(), "2")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- accounts, err := client.GetListAccounts(context.Background(), "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(accounts) != 2 {
- t.Fatalf("result should be two: %d", len(accounts))
- }
- if accounts[0].Username != "foo" {
- t.Fatalf("want %q but %q", "foo", accounts[0].Username)
- }
- if accounts[1].Username != "bar" {
- t.Fatalf("want %q but %q", "bar", accounts[1].Username)
- }
-}
-
-func TestGetList(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/lists/1" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `{"id": "1", "title": "foo"}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetList(context.Background(), "2")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- list, err := client.GetList(context.Background(), "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if list.Title != "foo" {
- t.Fatalf("want %q but %q", "foo", list.Title)
- }
-}
-
-func TestCreateList(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.PostFormValue("title") != "foo" {
- http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
- return
- }
- fmt.Fprintln(w, `{"id": "1", "title": "foo"}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.CreateList(context.Background(), "")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- list, err := client.CreateList(context.Background(), "foo")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if list.Title != "foo" {
- t.Fatalf("want %q but %q", "foo", list.Title)
- }
-}
-
-func TestRenameList(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/lists/1" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- if r.PostFormValue("title") != "bar" {
- http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
- return
- }
- fmt.Fprintln(w, `{"id": "1", "title": "bar"}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.RenameList(context.Background(), "2", "bar")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- list, err := client.RenameList(context.Background(), "1", "bar")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if list.Title != "bar" {
- t.Fatalf("want %q but %q", "bar", list.Title)
- }
-}
-
-func TestDeleteList(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/lists/1" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- if r.Method != "DELETE" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusMethodNotAllowed)
- return
- }
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- err := client.DeleteList(context.Background(), "2")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- err = client.DeleteList(context.Background(), "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-}
-
-func TestAddToList(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/lists/1/accounts" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- if r.PostFormValue("account_ids") != "1" {
- http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
- return
- }
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- err := client.AddToList(context.Background(), "1", "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-}
-
-func TestRemoveFromList(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/lists/1/accounts" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- if r.Method != "DELETE" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusMethodNotAllowed)
- return
- }
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- err := client.RemoveFromList(context.Background(), "1", "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-}
diff --git a/mastodon.go b/mastodon.go
index c704e10..a81d9bd 100644
--- a/mastodon.go
+++ b/mastodon.go
@@ -2,16 +2,20 @@
package mastodon
import (
+ "bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
+ "mime/multipart"
"net/http"
"net/url"
+ "os"
"path"
+ "path/filepath"
+ "strconv"
"strings"
- "time"
"github.com/tomnomnom/linkheader"
)
@@ -27,12 +31,11 @@ type Config struct {
// Client is a API client for mastodon.
type Client struct {
http.Client
- Config *Config
- UserAgent string
+ config *Config
}
func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}, pg *Pagination) error {
- u, err := url.Parse(c.Config.Server)
+ u, err := url.Parse(c.config.Server)
if err != nil {
return err
}
@@ -54,18 +57,32 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in
if err != nil {
return err
}
- } else if media, ok := params.(*Media); ok {
- r, contentType, err := media.bodyAndContentType()
+ } else if file, ok := params.(string); ok {
+ f, err := os.Open(file)
if err != nil {
return err
}
+ defer f.Close()
- req, err = http.NewRequest(method, u.String(), r)
+ var buf bytes.Buffer
+ mw := multipart.NewWriter(&buf)
+ part, err := mw.CreateFormFile("file", filepath.Base(file))
if err != nil {
return err
}
-
- ct = contentType
+ _, err = io.Copy(part, f)
+ if err != nil {
+ return err
+ }
+ err = mw.Close()
+ if err != nil {
+ return err
+ }
+ req, err = http.NewRequest(method, u.String(), &buf)
+ if err != nil {
+ return err
+ }
+ ct = mw.FormDataContentType()
} else {
if method == http.MethodGet && pg != nil {
u.RawQuery = pg.toValues().Encode()
@@ -76,41 +93,16 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in
}
}
req = req.WithContext(ctx)
- req.Header.Set("Authorization", "Bearer "+c.Config.AccessToken)
+ req.Header.Set("Authorization", "Bearer "+c.config.AccessToken)
if params != nil {
req.Header.Set("Content-Type", ct)
}
- if c.UserAgent != "" {
- req.Header.Set("User-Agent", c.UserAgent)
- }
-
- var resp *http.Response
- backoff := time.Second
- for {
- resp, err = c.Do(req)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
-
- // handle status code 429, which indicates the server is throttling
- // our requests. Do an exponential backoff and retry the request.
- if resp.StatusCode == 429 {
- if backoff > time.Hour {
- break
- }
-
- select {
- case <-time.After(backoff):
- case <-ctx.Done():
- return ctx.Err()
- }
-
- backoff = time.Duration(1.5 * float64(backoff))
- continue
- }
- break
+
+ resp, err := c.Do(req)
+ if err != nil {
+ return err
}
+ defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return parseAPIError("bad request", resp)
@@ -128,57 +120,25 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in
return json.NewDecoder(resp.Body).Decode(&res)
}
-// NewClient returns a new mastodon API client.
+// NewClient return new mastodon API client.
func NewClient(config *Config) *Client {
return &Client{
Client: *http.DefaultClient,
- Config: config,
+ config: config,
}
}
-// Authenticate gets access-token to the API.
+// Authenticate get access-token to the API.
func (c *Client) Authenticate(ctx context.Context, username, password string) error {
- params := url.Values{
- "client_id": {c.Config.ClientID},
- "client_secret": {c.Config.ClientSecret},
- "grant_type": {"password"},
- "username": {username},
- "password": {password},
- "scope": {"read write follow"},
- }
+ params := url.Values{}
+ params.Set("client_id", c.config.ClientID)
+ params.Set("client_secret", c.config.ClientSecret)
+ params.Set("grant_type", "password")
+ params.Set("username", username)
+ params.Set("password", password)
+ params.Set("scope", "read write follow")
- return c.authenticate(ctx, params)
-}
-
-// AuthenticateApp logs in using client credentials.
-func (c *Client) AuthenticateApp(ctx context.Context) error {
- params := url.Values{
- "client_id": {c.Config.ClientID},
- "client_secret": {c.Config.ClientSecret},
- "grant_type": {"client_credentials"},
- "redirect_uri": {"urn:ietf:wg:oauth:2.0:oob"},
- }
-
- return c.authenticate(ctx, params)
-}
-
-// AuthenticateToken logs in using a grant token returned by Application.AuthURI.
-//
-// redirectURI should be the same as Application.RedirectURI.
-func (c *Client) AuthenticateToken(ctx context.Context, authCode, redirectURI string) error {
- params := url.Values{
- "client_id": {c.Config.ClientID},
- "client_secret": {c.Config.ClientSecret},
- "grant_type": {"authorization_code"},
- "code": {authCode},
- "redirect_uri": {redirectURI},
- }
-
- return c.authenticate(ctx, params)
-}
-
-func (c *Client) authenticate(ctx context.Context, params url.Values) error {
- u, err := url.Parse(c.Config.Server)
+ u, err := url.Parse(c.config.Server)
if err != nil {
return err
}
@@ -190,9 +150,6 @@ func (c *Client) authenticate(ctx context.Context, params url.Values) error {
}
req = req.WithContext(ctx)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- if c.UserAgent != "" {
- req.Header.Set("User-Agent", c.UserAgent)
- }
resp, err := c.Do(req)
if err != nil {
return err
@@ -203,44 +160,25 @@ func (c *Client) authenticate(ctx context.Context, params url.Values) error {
return parseAPIError("bad authorization", resp)
}
- var res struct {
+ res := struct {
AccessToken string `json:"access_token"`
- }
+ }{}
err = json.NewDecoder(resp.Body).Decode(&res)
if err != nil {
return err
}
- c.Config.AccessToken = res.AccessToken
+ c.config.AccessToken = res.AccessToken
return nil
}
-// Convenience constants for Toot.Visibility
-const (
- VisibilityPublic = "public"
- VisibilityUnlisted = "unlisted"
- VisibilityFollowersOnly = "private"
- VisibilityDirectMessage = "direct"
-)
-
-// Toot is a struct to post status.
+// Toot is struct to post status.
type Toot struct {
- Status string `json:"status"`
- InReplyToID ID `json:"in_reply_to_id"`
- MediaIDs []ID `json:"media_ids"`
- Sensitive bool `json:"sensitive"`
- SpoilerText string `json:"spoiler_text"`
- Visibility string `json:"visibility"`
- Language string `json:"language"`
- ScheduledAt *time.Time `json:"scheduled_at,omitempty"`
- Poll *TootPoll `json:"poll"`
-}
-
-// TootPoll holds information for creating a poll in Toot.
-type TootPoll struct {
- Options []string `json:"options"`
- ExpiresInSeconds int64 `json:"expires_in"`
- Multiple bool `json:"multiple"`
- HideTotals bool `json:"hide_totals"`
+ Status string `json:"status"`
+ InReplyToID int64 `json:"in_reply_to_id"`
+ MediaIDs []int64 `json:"media_ids"`
+ Sensitive bool `json:"sensitive"`
+ SpoilerText string `json:"spoiler_text"`
+ Visibility string `json:"visibility"`
}
// Mention hold information for mention.
@@ -248,69 +186,36 @@ type Mention struct {
URL string `json:"url"`
Username string `json:"username"`
Acct string `json:"acct"`
- ID ID `json:"id"`
+ ID int64 `json:"id"`
}
// Tag hold information for tag.
type Tag struct {
- Name string `json:"name"`
- URL string `json:"url"`
- History []History `json:"history"`
-}
-
-// History hold information for history.
-type History struct {
- Day string `json:"day"`
- Uses string `json:"uses"`
- Accounts string `json:"accounts"`
+ Name string `json:"name"`
+ URL string `json:"url"`
}
// Attachment hold information for attachment.
type Attachment struct {
- ID ID `json:"id"`
- Type string `json:"type"`
- URL string `json:"url"`
- RemoteURL string `json:"remote_url"`
- PreviewURL string `json:"preview_url"`
- TextURL string `json:"text_url"`
- Description string `json:"description"`
- Meta AttachmentMeta `json:"meta"`
-}
-
-// AttachmentMeta holds information for attachment metadata.
-type AttachmentMeta struct {
- Original AttachmentSize `json:"original"`
- Small AttachmentSize `json:"small"`
-}
-
-// AttachmentSize holds information for attatchment size.
-type AttachmentSize struct {
- Width int64 `json:"width"`
- Height int64 `json:"height"`
- Size string `json:"size"`
- Aspect float64 `json:"aspect"`
-}
-
-// Emoji hold information for CustomEmoji.
-type Emoji struct {
- ShortCode string `json:"shortcode"`
- StaticURL string `json:"static_url"`
- URL string `json:"url"`
- VisibleInPicker bool `json:"visible_in_picker"`
+ ID int64 `json:"id"`
+ Type string `json:"type"`
+ URL string `json:"url"`
+ RemoteURL string `json:"remote_url"`
+ PreviewURL string `json:"preview_url"`
+ TextURL string `json:"text_url"`
}
// Results hold information for search result.
type Results struct {
Accounts []*Account `json:"accounts"`
Statuses []*Status `json:"statuses"`
- Hashtags []*Tag `json:"hashtags"`
+ Hashtags []string `json:"hashtags"`
}
// Pagination is a struct for specifying the get range.
type Pagination struct {
- MaxID ID
- SinceID ID
- MinID ID
+ MaxID int64
+ SinceID int64
Limit int64
}
@@ -334,25 +239,24 @@ func newPagination(rawlink string) (*Pagination, error) {
return nil, err
}
p.SinceID = sinceID
-
- minID, err := getPaginationID(link.URL, "min_id")
- if err != nil {
- return nil, err
- }
- p.MinID = minID
}
}
return p, nil
}
-func getPaginationID(rawurl, key string) (ID, error) {
+func getPaginationID(rawurl, key string) (int64, error) {
u, err := url.Parse(rawurl)
if err != nil {
- return "", err
+ return 0, err
}
- return ID(u.Query().Get(key)), nil
+ id, err := strconv.ParseInt(u.Query().Get(key), 10, 64)
+ if err != nil {
+ return 0, err
+ }
+
+ return id, nil
}
func (p *Pagination) toValues() url.Values {
@@ -360,14 +264,10 @@ func (p *Pagination) toValues() url.Values {
}
func (p *Pagination) setValues(params url.Values) url.Values {
- if p.MaxID != "" {
- params.Set("max_id", string(p.MaxID))
- }
- if p.SinceID != "" {
- params.Set("since_id", string(p.SinceID))
- }
- if p.MinID != "" {
- params.Set("min_id", string(p.MinID))
+ if p.MaxID > 0 {
+ params.Set("max_id", fmt.Sprint(p.MaxID))
+ } else if p.SinceID > 0 {
+ params.Set("since_id", fmt.Sprint(p.SinceID))
}
if p.Limit > 0 {
params.Set("limit", fmt.Sprint(p.Limit))
diff --git a/mastodon_test.go b/mastodon_test.go
index 5d14a44..eb6c476 100644
--- a/mastodon_test.go
+++ b/mastodon_test.go
@@ -2,7 +2,6 @@ package mastodon
import (
"context"
- "encoding/json"
"fmt"
"io"
"net/http"
@@ -26,26 +25,26 @@ func TestDoAPI(t *testing.T) {
c := NewClient(&Config{Server: ts.URL})
var accounts []Account
err := c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, &Pagination{
- MaxID: "999",
+ MaxID: 999,
})
if err == nil {
t.Fatalf("should be fail: %v", err)
}
pg := &Pagination{
- MaxID: "123",
- SinceID: "789",
+ MaxID: 123,
+ SinceID: 789,
Limit: 10,
}
err = c.doAPI(context.Background(), http.MethodGet, "/", url.Values{}, &accounts, pg)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- if pg.MaxID != "234" {
- t.Fatalf("want %q but %q", "234", pg.MaxID)
+ if pg.MaxID != 234 {
+ t.Fatalf("want %d but %d", 234, pg.MaxID)
}
- if pg.SinceID != "890" {
- t.Fatalf("want %q but %q", "890", pg.SinceID)
+ if pg.SinceID != 890 {
+ t.Fatalf("want %d but %d", 890, pg.SinceID)
}
if accounts[0].Username != "foo" {
t.Fatalf("want %q but %q", "foo", accounts[0].Username)
@@ -55,19 +54,19 @@ func TestDoAPI(t *testing.T) {
}
pg = &Pagination{
- MaxID: "123",
- SinceID: "789",
+ MaxID: 123,
+ SinceID: 789,
Limit: 10,
}
err = c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, pg)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- if pg.MaxID != "234" {
- t.Fatalf("want %q but %q", "234", pg.MaxID)
+ if pg.MaxID != 234 {
+ t.Fatalf("want %d but %d", 234, pg.MaxID)
}
- if pg.SinceID != "890" {
- t.Fatalf("want %q but %q", "890", pg.SinceID)
+ if pg.SinceID != 890 {
+ t.Fatalf("want %d but %d", 890, pg.SinceID)
}
if accounts[0].Username != "foo" {
t.Fatalf("want %q but %q", "foo", accounts[0].Username)
@@ -96,6 +95,7 @@ func TestAuthenticate(t *testing.T) {
return
}
fmt.Fprintln(w, `{"access_token": "zoo"}`)
+ return
}))
defer ts.Close()
@@ -123,6 +123,7 @@ func TestAuthenticate(t *testing.T) {
func TestAuthenticateWithCancel(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
+ return
}))
defer ts.Close()
@@ -137,42 +138,11 @@ func TestAuthenticateWithCancel(t *testing.T) {
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- if want := fmt.Sprintf("Post %q: context canceled", ts.URL+"/oauth/token"); want != err.Error() {
+ if want := "Post " + ts.URL + "/oauth/token: context canceled"; want != err.Error() {
t.Fatalf("want %q but %q", want, err.Error())
}
}
-func TestAuthenticateApp(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.FormValue("client_id") != "foo" || r.FormValue("client_secret") != "bar" {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- return
- }
- fmt.Fprintln(w, `{"name":"zzz","website":"yyy","vapid_key":"xxx"}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bat",
- })
- err := client.AuthenticateApp(context.Background())
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
-
- client = NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- })
- err = client.AuthenticateApp(context.Background())
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-}
-
func TestPostStatus(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") != "Bearer zoo" {
@@ -180,6 +150,7 @@ func TestPostStatus(t *testing.T) {
return
}
fmt.Fprintln(w, `{"access_token": "zoo"}`)
+ return
}))
defer ts.Close()
@@ -212,6 +183,7 @@ func TestPostStatus(t *testing.T) {
func TestPostStatusWithCancel(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
+ return
}))
defer ts.Close()
@@ -228,309 +200,15 @@ func TestPostStatusWithCancel(t *testing.T) {
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- if want := fmt.Sprintf("Post %q: context canceled", ts.URL+"/api/v1/statuses"); want != err.Error() {
+ if want := "Post " + ts.URL + "/api/v1/statuses: context canceled"; want != err.Error() {
t.Fatalf("want %q but %q", want, err.Error())
}
}
-func TestPostStatusParams(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/statuses" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- r.ParseForm()
- if r.FormValue("media_ids[]") != "" && r.FormValue("poll[options][]") != "" {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- }
- s := Status{
- ID: ID("1"),
- Content: fmt.Sprintf("%s
", r.FormValue("status")),
- }
- if r.FormValue("in_reply_to_id") != "" {
- s.InReplyToID = ID(r.FormValue("in_reply_to_id"))
- }
- if r.FormValue("visibility") != "" {
- s.Visibility = (r.FormValue("visibility"))
- }
- if r.FormValue("language") != "" {
- s.Language = (r.FormValue("language"))
- }
- if r.FormValue("sensitive") == "true" {
- s.Sensitive = true
- s.SpoilerText = fmt.Sprintf("%s
", r.FormValue("spoiler_text"))
- }
- if r.FormValue("media_ids[]") != "" {
- for _, id := range r.Form["media_ids[]"] {
- s.MediaAttachments = append(s.MediaAttachments,
- Attachment{ID: ID(id)})
- }
- }
- if r.FormValue("poll[options][]") != "" {
- p := Poll{}
- for _, opt := range r.Form["poll[options][]"] {
- p.Options = append(p.Options, PollOption{
- Title: opt,
- VotesCount: 0,
- })
- }
- if r.FormValue("poll[multiple]") == "true" {
- p.Multiple = true
- }
- s.Poll = &p
- }
- json.NewEncoder(w).Encode(s)
- }))
- defer ts.Close()
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- s, err := client.PostStatus(context.Background(), &Toot{
- Status: "foobar",
- InReplyToID: ID("2"),
- Visibility: "unlisted",
- Language: "sv",
- Sensitive: true,
- SpoilerText: "bar",
- MediaIDs: []ID{"1", "2"},
- Poll: &TootPoll{
- Options: []string{"A", "B"},
- },
- })
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(s.MediaAttachments) > 0 && s.Poll != nil {
- t.Fatal("should not fail, can't have both Media and Poll")
- }
- if s.Content != "foobar
" {
- t.Fatalf("want %q but %q", "foobar
", s.Content)
- }
- if s.InReplyToID != "2" {
- t.Fatalf("want %q but %q", "2", s.InReplyToID)
- }
- if s.Visibility != "unlisted" {
- t.Fatalf("want %q but %q", "unlisted", s.Visibility)
- }
- if s.Language != "sv" {
- t.Fatalf("want %q but %q", "sv", s.Language)
- }
- if s.Sensitive != true {
- t.Fatalf("want %t but %t", true, s.Sensitive)
- }
- if s.SpoilerText != "bar
" {
- t.Fatalf("want %q but %q", "bar
", s.SpoilerText)
- }
- s, err = client.PostStatus(context.Background(), &Toot{
- Status: "foobar",
- Poll: &TootPoll{
- Multiple: true,
- Options: []string{"A", "B"},
- HideTotals: true,
- },
- })
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if s.Poll == nil {
- t.Fatalf("poll should not be %v", s.Poll)
- }
- if len(s.Poll.Options) != 2 {
- t.Fatalf("want %q but %q", 2, len(s.Poll.Options))
- }
- if s.Poll.Options[0].Title != "A" {
- t.Fatalf("want %q but %q", "A", s.Poll.Options[0].Title)
- }
- if s.Poll.Options[1].Title != "B" {
- t.Fatalf("want %q but %q", "B", s.Poll.Options[1].Title)
- }
- if s.Poll.Multiple != true {
- t.Fatalf("want %t but %t", true, s.Poll.Multiple)
- }
-}
-
-func TestUpdateStatus(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Header.Get("Authorization") != "Bearer zoo" {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- return
- }
- fmt.Fprintln(w, `{"access_token": "zoo"}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- })
- _, err := client.UpdateStatus(context.Background(), &Toot{
- Status: "foobar",
- }, ID("1"))
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
-
- client = NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err = client.UpdateStatus(context.Background(), &Toot{
- Status: "foobar",
- }, ID("1"))
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-}
-
-func TestUpdateStatusWithCancel(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- time.Sleep(3 * time.Second)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- })
- ctx, cancel := context.WithCancel(context.Background())
- cancel()
- _, err := client.UpdateStatus(ctx, &Toot{
- Status: "foobar",
- }, ID("1"))
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- if want := fmt.Sprintf("Put %q: context canceled", ts.URL+"/api/v1/statuses/1"); want != err.Error() {
- t.Fatalf("want %q but %q", want, err.Error())
- }
-}
-func TestUpdateStatusParams(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/statuses/1" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- r.ParseForm()
- if r.FormValue("media_ids[]") != "" && r.FormValue("poll[options][]") != "" {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- }
- s := Status{
- ID: ID("1"),
- Content: fmt.Sprintf("%s
", r.FormValue("status")),
- }
- if r.FormValue("in_reply_to_id") != "" {
- s.InReplyToID = ID(r.FormValue("in_reply_to_id"))
- }
- if r.FormValue("visibility") != "" {
- s.Visibility = (r.FormValue("visibility"))
- }
- if r.FormValue("language") != "" {
- s.Language = (r.FormValue("language"))
- }
- if r.FormValue("sensitive") == "true" {
- s.Sensitive = true
- s.SpoilerText = fmt.Sprintf("%s
", r.FormValue("spoiler_text"))
- }
- if r.FormValue("media_ids[]") != "" {
- for _, id := range r.Form["media_ids[]"] {
- s.MediaAttachments = append(s.MediaAttachments,
- Attachment{ID: ID(id)})
- }
- }
- if r.FormValue("poll[options][]") != "" {
- p := Poll{}
- for _, opt := range r.Form["poll[options][]"] {
- p.Options = append(p.Options, PollOption{
- Title: opt,
- VotesCount: 0,
- })
- }
- if r.FormValue("poll[multiple]") == "true" {
- p.Multiple = true
- }
- s.Poll = &p
- }
- json.NewEncoder(w).Encode(s)
- }))
- defer ts.Close()
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- s, err := client.UpdateStatus(context.Background(), &Toot{
- Status: "foobar",
- InReplyToID: ID("2"),
- Visibility: "unlisted",
- Language: "sv",
- Sensitive: true,
- SpoilerText: "bar",
- MediaIDs: []ID{"1", "2"},
- Poll: &TootPoll{
- Options: []string{"A", "B"},
- },
- }, ID("1"))
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(s.MediaAttachments) > 0 && s.Poll != nil {
- t.Fatal("should not fail, can't have both Media and Poll")
- }
- if s.Content != "foobar
" {
- t.Fatalf("want %q but %q", "foobar
", s.Content)
- }
- if s.InReplyToID != "2" {
- t.Fatalf("want %q but %q", "2", s.InReplyToID)
- }
- if s.Visibility != "unlisted" {
- t.Fatalf("want %q but %q", "unlisted", s.Visibility)
- }
- if s.Language != "sv" {
- t.Fatalf("want %q but %q", "sv", s.Language)
- }
- if s.Sensitive != true {
- t.Fatalf("want %t but %t", true, s.Sensitive)
- }
- if s.SpoilerText != "bar
" {
- t.Fatalf("want %q but %q", "bar
", s.SpoilerText)
- }
- s, err = client.UpdateStatus(context.Background(), &Toot{
- Status: "foobar",
- Poll: &TootPoll{
- Multiple: true,
- Options: []string{"A", "B"},
- },
- }, ID("1"))
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if s.Poll == nil {
- t.Fatalf("poll should not be %v", s.Poll)
- }
- if len(s.Poll.Options) != 2 {
- t.Fatalf("want %q but %q", 2, len(s.Poll.Options))
- }
- if s.Poll.Options[0].Title != "A" {
- t.Fatalf("want %q but %q", "A", s.Poll.Options[0].Title)
- }
- if s.Poll.Options[1].Title != "B" {
- t.Fatalf("want %q but %q", "B", s.Poll.Options[1].Title)
- }
- if s.Poll.Multiple != true {
- t.Fatalf("want %t but %t", true, s.Poll.Multiple)
- }
-}
func TestGetTimelineHome(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `[{"content": "foo"}, {"content": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -570,6 +248,7 @@ func TestGetTimelineHome(t *testing.T) {
func TestGetTimelineHomeWithCancel(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
+ return
}))
defer ts.Close()
@@ -585,14 +264,13 @@ func TestGetTimelineHomeWithCancel(t *testing.T) {
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- if want := fmt.Sprintf("Get %q: context canceled", ts.URL+"/api/v1/timelines/home"); want != err.Error() {
+ if want := "Get " + ts.URL + "/api/v1/timelines/home: context canceled"; want != err.Error() {
t.Fatalf("want %q but %q", want, err.Error())
}
}
func TestForTheCoverages(t *testing.T) {
(*UpdateEvent)(nil).event()
- (*UpdateEditEvent)(nil).event()
(*NotificationEvent)(nil).event()
(*DeleteEvent)(nil).event()
(*ErrorEvent)(nil).event()
@@ -615,20 +293,15 @@ func TestNewPagination(t *testing.T) {
t.Fatalf("should be fail: %v", err)
}
- _, err = newPagination(`; rel="prev"`)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-
pg, err := newPagination(`; rel="next", ; rel="prev"`)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- if pg.MaxID != "123" {
- t.Fatalf("want %q but %q", "123", pg.MaxID)
+ if pg.MaxID != 123 {
+ t.Fatalf("want %d but %d", 123, pg.MaxID)
}
- if pg.SinceID != "789" {
- t.Fatalf("want %q but %q", "789", pg.SinceID)
+ if pg.SinceID != 789 {
+ t.Fatalf("want %d but %d", 789, pg.SinceID)
}
}
@@ -639,24 +312,23 @@ func TestGetPaginationID(t *testing.T) {
}
_, err = getPaginationID("http://example.com?max_id=abc", "max_id")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
+ if err == nil {
+ t.Fatalf("should be fail: %v", err)
}
id, err := getPaginationID("http://example.com?max_id=123", "max_id")
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- if id != "123" {
- t.Fatalf("want %q but %q", "123", id)
+ if id != 123 {
+ t.Fatalf("want %d but %d", 123, id)
}
}
func TestPaginationSetValues(t *testing.T) {
p := &Pagination{
- MaxID: "123",
- SinceID: "456",
- MinID: "789",
+ MaxID: 123,
+ SinceID: 789,
Limit: 10,
}
before := url.Values{"key": {"value"}}
@@ -667,19 +339,16 @@ func TestPaginationSetValues(t *testing.T) {
if after.Get("max_id") != "123" {
t.Fatalf("want %q but %q", "123", after.Get("max_id"))
}
- if after.Get("since_id") != "456" {
- t.Fatalf("want %q but %q", "456", after.Get("since_id"))
- }
- if after.Get("min_id") != "789" {
- t.Fatalf("want %q but %q", "789", after.Get("min_id"))
+ if after.Get("since_id") != "" {
+ t.Fatalf("result should be empty string: %q", after.Get("since_id"))
}
if after.Get("limit") != "10" {
t.Fatalf("want %q but %q", "10", after.Get("limit"))
}
p = &Pagination{
- MaxID: "",
- SinceID: "789",
+ MaxID: 0,
+ SinceID: 789,
}
before = url.Values{}
after = p.setValues(before)
@@ -689,7 +358,4 @@ func TestPaginationSetValues(t *testing.T) {
if after.Get("since_id") != "789" {
t.Fatalf("want %q but %q", "789", after.Get("since_id"))
}
- if after.Get("min_id") != "" {
- t.Fatalf("result should be empty string: %q", after.Get("min_id"))
- }
}
diff --git a/notification.go b/notification.go
index fa0cab9..15e6cf7 100644
--- a/notification.go
+++ b/notification.go
@@ -2,40 +2,21 @@ package mastodon
import (
"context"
- "crypto/ecdsa"
- "crypto/elliptic"
- "encoding/base64"
"fmt"
"net/http"
- "net/url"
- "strconv"
"time"
)
-// Notification holds information for a mastodon notification.
+// Notification hold information for mastodon notification.
type Notification struct {
- ID ID `json:"id"`
+ ID int64 `json:"id"`
Type string `json:"type"`
CreatedAt time.Time `json:"created_at"`
Account Account `json:"account"`
Status *Status `json:"status"`
}
-type PushSubscription struct {
- ID ID `json:"id"`
- Endpoint string `json:"endpoint"`
- ServerKey string `json:"server_key"`
- Alerts *PushAlerts `json:"alerts"`
-}
-
-type PushAlerts struct {
- Follow *Sbool `json:"follow"`
- Favourite *Sbool `json:"favourite"`
- Reblog *Sbool `json:"reblog"`
- Mention *Sbool `json:"mention"`
-}
-
-// GetNotifications returns notifications.
+// GetNotifications return notifications.
func (c *Client) GetNotifications(ctx context.Context, pg *Pagination) ([]*Notification, error) {
var notifications []*Notification
err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, ¬ifications, pg)
@@ -45,87 +26,17 @@ func (c *Client) GetNotifications(ctx context.Context, pg *Pagination) ([]*Notif
return notifications, nil
}
-// GetNotification returns notification.
-func (c *Client) GetNotification(ctx context.Context, id ID) (*Notification, error) {
+// GetNotification return notification.
+func (c *Client) GetNotification(ctx context.Context, id int64) (*Notification, error) {
var notification Notification
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/notifications/%v", id), nil, ¬ification, nil)
+ err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/notifications/%d", id), nil, ¬ification, nil)
if err != nil {
return nil, err
}
return ¬ification, nil
}
-// DismissNotification deletes a single notification.
-func (c *Client) DismissNotification(ctx context.Context, id ID) error {
- return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/notifications/%v/dismiss", id), nil, nil, nil)
-}
-
-// ClearNotifications clears notifications.
+// ClearNotifications clear notifications.
func (c *Client) ClearNotifications(ctx context.Context) error {
return c.doAPI(ctx, http.MethodPost, "/api/v1/notifications/clear", nil, nil, nil)
}
-
-// AddPushSubscription adds a new push subscription.
-func (c *Client) AddPushSubscription(ctx context.Context, endpoint string, public ecdsa.PublicKey, shared []byte, alerts PushAlerts) (*PushSubscription, error) {
- var subscription PushSubscription
- pk := elliptic.Marshal(public.Curve, public.X, public.Y)
- params := url.Values{}
- params.Add("subscription[endpoint]", endpoint)
- params.Add("subscription[keys][p256dh]", base64.RawURLEncoding.EncodeToString(pk))
- params.Add("subscription[keys][auth]", base64.RawURLEncoding.EncodeToString(shared))
- if alerts.Follow != nil {
- params.Add("data[alerts][follow]", strconv.FormatBool(bool(*alerts.Follow)))
- }
- if alerts.Favourite != nil {
- params.Add("data[alerts][favourite]", strconv.FormatBool(bool(*alerts.Favourite)))
- }
- if alerts.Reblog != nil {
- params.Add("data[alerts][reblog]", strconv.FormatBool(bool(*alerts.Reblog)))
- }
- if alerts.Mention != nil {
- params.Add("data[alerts][mention]", strconv.FormatBool(bool(*alerts.Mention)))
- }
- err := c.doAPI(ctx, http.MethodPost, "/api/v1/push/subscription", params, &subscription, nil)
- if err != nil {
- return nil, err
- }
- return &subscription, nil
-}
-
-// UpdatePushSubscription updates which type of notifications are sent for the active push subscription.
-func (c *Client) UpdatePushSubscription(ctx context.Context, alerts *PushAlerts) (*PushSubscription, error) {
- var subscription PushSubscription
- params := url.Values{}
- if alerts.Follow != nil {
- params.Add("data[alerts][follow]", strconv.FormatBool(bool(*alerts.Follow)))
- }
- if alerts.Mention != nil {
- params.Add("data[alerts][favourite]", strconv.FormatBool(bool(*alerts.Favourite)))
- }
- if alerts.Reblog != nil {
- params.Add("data[alerts][reblog]", strconv.FormatBool(bool(*alerts.Reblog)))
- }
- if alerts.Mention != nil {
- params.Add("data[alerts][mention]", strconv.FormatBool(bool(*alerts.Mention)))
- }
- err := c.doAPI(ctx, http.MethodPut, "/api/v1/push/subscription", params, &subscription, nil)
- if err != nil {
- return nil, err
- }
- return &subscription, nil
-}
-
-// RemovePushSubscription deletes the active push subscription.
-func (c *Client) RemovePushSubscription(ctx context.Context) error {
- return c.doAPI(ctx, http.MethodDelete, "/api/v1/push/subscription", nil, nil, nil)
-}
-
-// GetPushSubscription retrieves information about the active push subscription.
-func (c *Client) GetPushSubscription(ctx context.Context) (*PushSubscription, error) {
- var subscription PushSubscription
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/push/subscription", nil, &subscription, nil)
- if err != nil {
- return nil, err
- }
- return &subscription, nil
-}
diff --git a/notification_test.go b/notification_test.go
index 78cdbdd..740dec9 100644
--- a/notification_test.go
+++ b/notification_test.go
@@ -2,9 +2,6 @@ package mastodon
import (
"context"
- "crypto/ecdsa"
- "crypto/elliptic"
- "crypto/rand"
"fmt"
"net/http"
"net/http/httptest"
@@ -23,11 +20,9 @@ func TestGetNotifications(t *testing.T) {
case "/api/v1/notifications/clear":
fmt.Fprintln(w, `{}`)
return
- case "/api/v1/notifications/123/dismiss":
- fmt.Fprintln(w, `{}`)
- return
}
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
+ return
}))
defer ts.Close()
@@ -44,100 +39,21 @@ func TestGetNotifications(t *testing.T) {
if len(ns) != 2 {
t.Fatalf("result should be two: %d", len(ns))
}
- if ns[0].ID != "122" {
- t.Fatalf("want %v but %v", "122", ns[0].ID)
+ if ns[0].ID != 122 {
+ t.Fatalf("want %v but %v", 122, ns[0].ID)
}
- if ns[1].ID != "123" {
- t.Fatalf("want %v but %v", "123", ns[1].ID)
+ if ns[1].ID != 123 {
+ t.Fatalf("want %v but %v", 123, ns[1].ID)
}
- n, err := client.GetNotification(context.Background(), "123")
+ n, err := client.GetNotification(context.Background(), 123)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- if n.ID != "123" {
- t.Fatalf("want %v but %v", "123", n.ID)
+ if n.ID != 123 {
+ t.Fatalf("want %v but %v", 123, n.ID)
}
err = client.ClearNotifications(context.Background())
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- err = client.DismissNotification(context.Background(), "123")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-}
-
-func TestPushSubscription(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- switch r.URL.Path {
- case "/api/v1/push/subscription":
- fmt.Fprintln(w, ` {"id":1,"endpoint":"https://example.org","alerts":{"follow":true,"favourite":"true","reblog":"true","mention":"true"},"server_key":"foobar"}`)
- return
- }
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
-
- enabled := new(Sbool)
- *enabled = true
- alerts := PushAlerts{Follow: enabled, Favourite: enabled, Reblog: enabled, Mention: enabled}
-
- priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
- if err != nil {
- t.Fatal(err)
- }
-
- shared := make([]byte, 16)
- _, err = rand.Read(shared)
- if err != nil {
- t.Fatal(err)
- }
-
- testSub := func(sub *PushSubscription, err error) {
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if sub.ID != "1" {
- t.Fatalf("want %v but %v", "1", sub.ID)
- }
- if sub.Endpoint != "https://example.org" {
- t.Fatalf("want %v but %v", "https://example.org", sub.Endpoint)
- }
- if sub.ServerKey != "foobar" {
- t.Fatalf("want %v but %v", "foobar", sub.ServerKey)
- }
- if *sub.Alerts.Favourite != true {
- t.Fatalf("want %v but %v", true, *sub.Alerts.Favourite)
- }
- if *sub.Alerts.Mention != true {
- t.Fatalf("want %v but %v", true, *sub.Alerts.Mention)
- }
- if *sub.Alerts.Reblog != true {
- t.Fatalf("want %v but %v", true, *sub.Alerts.Reblog)
- }
- if *sub.Alerts.Follow != true {
- t.Fatalf("want %v but %v", true, *sub.Alerts.Follow)
- }
- }
-
- sub, err := client.AddPushSubscription(context.Background(), "http://example.org", priv.PublicKey, shared, alerts)
- testSub(sub, err)
-
- sub, err = client.GetPushSubscription(context.Background())
- testSub(sub, err)
-
- sub, err = client.UpdatePushSubscription(context.Background(), &alerts)
- testSub(sub, err)
-
- err = client.RemovePushSubscription(context.Background())
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
}
diff --git a/polls.go b/polls.go
deleted file mode 100644
index 16be421..0000000
--- a/polls.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package mastodon
-
-import (
- "context"
- "fmt"
- "net/http"
- "net/url"
- "time"
-)
-
-// Poll holds information for mastodon polls.
-type Poll struct {
- ID ID `json:"id"`
- ExpiresAt time.Time `json:"expires_at"`
- Expired bool `json:"expired"`
- Multiple bool `json:"multiple"`
- VotesCount int64 `json:"votes_count"`
- VotersCount int64 `json:"voters_count"`
- Options []PollOption `json:"options"`
- Voted bool `json:"voted"`
- OwnVotes []int `json:"own_votes"`
- Emojis []Emoji `json:"emojis"`
-}
-
-// Poll holds information for a mastodon poll option.
-type PollOption struct {
- Title string `json:"title"`
- VotesCount int64 `json:"votes_count"`
-}
-
-// GetPoll returns poll specified by id.
-func (c *Client) GetPoll(ctx context.Context, id ID) (*Poll, error) {
- var poll Poll
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/polls/%s", id), nil, &poll, nil)
- if err != nil {
- return nil, err
- }
- return &poll, nil
-}
-
-// PollVote votes on a poll specified by id, choices is the Poll.Options index to vote on
-func (c *Client) PollVote(ctx context.Context, id ID, choices ...int) (*Poll, error) {
- params := url.Values{}
- for _, c := range choices {
- params.Add("choices[]", fmt.Sprintf("%d", c))
- }
-
- var poll Poll
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/polls/%s/votes", url.PathEscape(string(id))), params, &poll, nil)
- if err != nil {
- return nil, err
- }
- return &poll, nil
-}
diff --git a/polls_test.go b/polls_test.go
deleted file mode 100644
index 7ccee38..0000000
--- a/polls_test.go
+++ /dev/null
@@ -1,145 +0,0 @@
-package mastodon
-
-import (
- "context"
- "fmt"
- "net/http"
- "net/http/httptest"
- "testing"
-)
-
-func TestGetPoll(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/polls/1234567" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `{"id": "1234567", "expires_at": "2019-12-05T04:05:08.302Z", "expired": true, "multiple": false, "votes_count": 10, "voters_count": null, "voted": true, "own_votes": [1], "options": [{"title": "accept", "votes_count": 6}, {"title": "deny", "votes_count": 4}], "emojis":[{"shortcode":"💩", "url":"http://example.com", "static_url": "http://example.com/static"}]}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetPoll(context.Background(), "123")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- poll, err := client.GetPoll(context.Background(), "1234567")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if poll.Expired != true {
- t.Fatalf("want %t but %t", true, poll.Expired)
- }
- if poll.Multiple != false {
- t.Fatalf("want %t but %t", true, poll.Multiple)
- }
- if poll.VotesCount != 10 {
- t.Fatalf("want %d but %d", 10, poll.VotesCount)
- }
- if poll.VotersCount != 0 {
- t.Fatalf("want %d but %d", 0, poll.VotersCount)
- }
- if poll.Voted != true {
- t.Fatalf("want %t but %t", true, poll.Voted)
- }
- if len(poll.OwnVotes) != 1 {
- t.Fatalf("should have own votes")
- }
- if poll.OwnVotes[0] != 1 {
- t.Fatalf("want %d but %d", 1, poll.OwnVotes[0])
- }
- if len(poll.Options) != 2 {
- t.Fatalf("should have 2 options")
- }
- if poll.Options[0].Title != "accept" {
- t.Fatalf("want %q but %q", "accept", poll.Options[0].Title)
- }
- if poll.Options[0].VotesCount != 6 {
- t.Fatalf("want %q but %q", 6, poll.Options[0].VotesCount)
- }
- if poll.Options[1].Title != "deny" {
- t.Fatalf("want %q but %q", "deny", poll.Options[1].Title)
- }
- if poll.Options[1].VotesCount != 4 {
- t.Fatalf("want %q but %q", 4, poll.Options[1].VotesCount)
- }
- if len(poll.Emojis) != 1 {
- t.Fatal("should have emojis")
- }
- if poll.Emojis[0].ShortCode != "💩" {
- t.Fatalf("want %q but %q", "💩", poll.Emojis[0].ShortCode)
- }
- if poll.Emojis[0].URL != "http://example.com" {
- t.Fatalf("want %q but %q", "https://example.com", poll.Emojis[0].URL)
- }
- if poll.Emojis[0].StaticURL != "http://example.com/static" {
- t.Fatalf("want %q but %q", "https://example.com/static", poll.Emojis[0].StaticURL)
- }
-}
-
-func TestPollVote(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/polls/1234567/votes" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- if r.Method != "POST" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusMethodNotAllowed)
- return
- }
- fmt.Fprintln(w, `{"id": "1234567", "expires_at": "2019-12-05T04:05:08.302Z", "expired": false, "multiple": false, "votes_count": 10, "voters_count": null, "voted": true, "own_votes": [1], "options": [{"title": "accept", "votes_count": 6}, {"title": "deny", "votes_count": 4}], "emojis":[]}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- poll, err := client.PollVote(context.Background(), ID("1234567"), 1)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if poll.Expired != false {
- t.Fatalf("want %t but %t", false, poll.Expired)
- }
- if poll.Multiple != false {
- t.Fatalf("want %t but %t", true, poll.Multiple)
- }
- if poll.VotesCount != 10 {
- t.Fatalf("want %d but %d", 10, poll.VotesCount)
- }
- if poll.VotersCount != 0 {
- t.Fatalf("want %d but %d", 0, poll.VotersCount)
- }
- if poll.Voted != true {
- t.Fatalf("want %t but %t", true, poll.Voted)
- }
- if len(poll.OwnVotes) != 1 {
- t.Fatalf("should have own votes")
- }
- if poll.OwnVotes[0] != 1 {
- t.Fatalf("want %d but %d", 1, poll.OwnVotes[0])
- }
- if len(poll.Options) != 2 {
- t.Fatalf("should have 2 options")
- }
- if poll.Options[0].Title != "accept" {
- t.Fatalf("want %q but %q", "accept", poll.Options[0].Title)
- }
- if poll.Options[0].VotesCount != 6 {
- t.Fatalf("want %q but %q", 6, poll.Options[0].VotesCount)
- }
- if poll.Options[1].Title != "deny" {
- t.Fatalf("want %q but %q", "deny", poll.Options[1].Title)
- }
- if poll.Options[1].VotesCount != 4 {
- t.Fatalf("want %q but %q", 4, poll.Options[1].VotesCount)
- }
-}
diff --git a/report.go b/report.go
index 662c16a..1e6debf 100644
--- a/report.go
+++ b/report.go
@@ -2,17 +2,18 @@ package mastodon
import (
"context"
+ "fmt"
"net/http"
"net/url"
)
-// Report holds information for a mastodon report.
+// Report hold information for mastodon report.
type Report struct {
ID int64 `json:"id"`
ActionTaken bool `json:"action_taken"`
}
-// GetReports returns report of the current user.
+// GetReports return report of the current user.
func (c *Client) GetReports(ctx context.Context) ([]*Report, error) {
var reports []*Report
err := c.doAPI(ctx, http.MethodGet, "/api/v1/reports", nil, &reports, nil)
@@ -23,11 +24,11 @@ func (c *Client) GetReports(ctx context.Context) ([]*Report, error) {
}
// Report reports the report
-func (c *Client) Report(ctx context.Context, accountID ID, ids []ID, comment string) (*Report, error) {
+func (c *Client) Report(ctx context.Context, accountID int64, ids []int64, comment string) (*Report, error) {
params := url.Values{}
- params.Set("account_id", string(accountID))
+ params.Set("account_id", fmt.Sprint(accountID))
for _, id := range ids {
- params.Add("status_ids[]", string(id))
+ params.Add("status_ids[]", fmt.Sprint(id))
}
params.Set("comment", comment)
var report Report
diff --git a/report_test.go b/report_test.go
index f25f0b5..2d78bcf 100644
--- a/report_test.go
+++ b/report_test.go
@@ -15,6 +15,7 @@ func TestGetReports(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"id": 122, "action_taken": false}, {"id": 123, "action_taken": true}]`)
+ return
}))
defer ts.Close()
@@ -54,6 +55,7 @@ func TestReport(t *testing.T) {
} else {
fmt.Fprintln(w, `{"id": 1234, "action_taken": true}`)
}
+ return
}))
defer ts.Close()
@@ -63,26 +65,26 @@ func TestReport(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.Report(context.Background(), "121", nil, "")
+ rp, err := client.Report(context.Background(), 121, nil, "")
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- rp, err := client.Report(context.Background(), "122", nil, "")
+ rp, err = client.Report(context.Background(), 122, nil, "")
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
if rp.ID != 1234 {
- t.Fatalf("want %q but %q", "1234", rp.ID)
+ t.Fatalf("want %v but %v", 1234, rp.ID)
}
if rp.ActionTaken {
t.Fatalf("want %v but %v", true, rp.ActionTaken)
}
- rp, err = client.Report(context.Background(), "123", []ID{"567"}, "")
+ rp, err = client.Report(context.Background(), 123, []int64{567}, "")
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
if rp.ID != 1234 {
- t.Fatalf("want %q but %q", "1234", rp.ID)
+ t.Fatalf("want %v but %v", 1234, rp.ID)
}
if !rp.ActionTaken {
t.Fatalf("want %v but %v", false, rp.ActionTaken)
diff --git a/status.go b/status.go
index f12e4eb..38ea606 100644
--- a/status.go
+++ b/status.go
@@ -1,165 +1,53 @@
package mastodon
import (
- "bytes"
"context"
"fmt"
- "io"
- "mime/multipart"
"net/http"
"net/url"
- "os"
- "strings"
+ "strconv"
"time"
)
// Status is struct to hold status.
type Status struct {
ID ID `json:"id"`
- URI string `json:"uri"`
- URL string `json:"url"`
- Account Account `json:"account"`
+ CreatedAt time.Time `json:"created_at"`
InReplyToID interface{} `json:"in_reply_to_id"`
InReplyToAccountID interface{} `json:"in_reply_to_account_id"`
- Reblog *Status `json:"reblog"`
- Content string `json:"content"`
- CreatedAt time.Time `json:"created_at"`
- EditedAt time.Time `json:"edited_at"`
- Emojis []Emoji `json:"emojis"`
- RepliesCount int64 `json:"replies_count"`
- ReblogsCount int64 `json:"reblogs_count"`
- FavouritesCount int64 `json:"favourites_count"`
- Reblogged interface{} `json:"reblogged"`
- Favourited interface{} `json:"favourited"`
- Bookmarked interface{} `json:"bookmarked"`
- Muted interface{} `json:"muted"`
Sensitive bool `json:"sensitive"`
SpoilerText string `json:"spoiler_text"`
Visibility string `json:"visibility"`
+ Application Application `json:"application"`
+ Account Account `json:"account"`
MediaAttachments []Attachment `json:"media_attachments"`
Mentions []Mention `json:"mentions"`
Tags []Tag `json:"tags"`
- Card *Card `json:"card"`
- Poll *Poll `json:"poll"`
- Application Application `json:"application"`
- Language string `json:"language"`
- Pinned interface{} `json:"pinned"`
+ URI string `json:"uri"`
+ Content string `json:"content"`
+ URL string `json:"url"`
+ ReblogsCount int64 `json:"reblogs_count"`
+ FavouritesCount int64 `json:"favourites_count"`
+ Reblog *Status `json:"reblog"`
+ Favourited interface{} `json:"favourited"`
+ Reblogged interface{} `json:"reblogged"`
}
-// StatusHistory is a struct to hold status history data.
-type StatusHistory struct {
- Content string `json:"content"`
- SpoilerText string `json:"spoiler_text"`
- Account Account `json:"account"`
- Sensitive bool `json:"sensitive"`
- CreatedAt time.Time `json:"created_at"`
- Emojis []Emoji `json:"emojis"`
- MediaAttachments []Attachment `json:"media_attachments"`
-}
-
-// Context holds information for a mastodon context.
+// Context hold information for mastodon context.
type Context struct {
Ancestors []*Status `json:"ancestors"`
Descendants []*Status `json:"descendants"`
}
-// Card holds information for a mastodon card.
+// Card hold information for mastodon card.
type Card struct {
- URL string `json:"url"`
- Title string `json:"title"`
- Description string `json:"description"`
- Image string `json:"image"`
- Type string `json:"type"`
- AuthorName string `json:"author_name"`
- AuthorURL string `json:"author_url"`
- ProviderName string `json:"provider_name"`
- ProviderURL string `json:"provider_url"`
- HTML string `json:"html"`
- Width int64 `json:"width"`
- Height int64 `json:"height"`
+ URL string `json:"url"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ Image string `json:"image"`
}
-// Source holds source properties so a status can be edited.
-type Source struct {
- ID ID `json:"id"`
- Text string `json:"text"`
- SpoilerText string `json:"spoiler_text"`
-}
-
-// Conversation holds information for a mastodon conversation.
-type Conversation struct {
- ID ID `json:"id"`
- Accounts []*Account `json:"accounts"`
- Unread bool `json:"unread"`
- LastStatus *Status `json:"last_status"`
-}
-
-// Media is struct to hold media.
-type Media struct {
- File io.Reader
- Thumbnail io.Reader
- Description string
- Focus string
-}
-
-func (m *Media) bodyAndContentType() (io.Reader, string, error) {
- var buf bytes.Buffer
- mw := multipart.NewWriter(&buf)
-
- fileName := "upload"
- if f, ok := m.File.(*os.File); ok {
- fileName = f.Name()
- }
- file, err := mw.CreateFormFile("file", fileName)
- if err != nil {
- return nil, "", err
- }
- if _, err := io.Copy(file, m.File); err != nil {
- return nil, "", err
- }
-
- if m.Thumbnail != nil {
- thumbName := "upload"
- if f, ok := m.Thumbnail.(*os.File); ok {
- thumbName = f.Name()
- }
- thumb, err := mw.CreateFormFile("thumbnail", thumbName)
- if err != nil {
- return nil, "", err
- }
- if _, err := io.Copy(thumb, m.Thumbnail); err != nil {
- return nil, "", err
- }
- }
-
- if m.Description != "" {
- desc, err := mw.CreateFormField("description")
- if err != nil {
- return nil, "", err
- }
- if _, err := io.Copy(desc, strings.NewReader(m.Description)); err != nil {
- return nil, "", err
- }
- }
-
- if m.Focus != "" {
- focus, err := mw.CreateFormField("focus")
- if err != nil {
- return nil, "", err
- }
- if _, err := io.Copy(focus, strings.NewReader(m.Focus)); err != nil {
- return nil, "", err
- }
- }
-
- if err := mw.Close(); err != nil {
- return nil, "", err
- }
-
- return &buf, mw.FormDataContentType(), nil
-}
-
-// GetFavourites returns the favorite list of the current user.
+// GetFavourites return the favorite list of the current user.
func (c *Client) GetFavourites(ctx context.Context, pg *Pagination) ([]*Status, error) {
var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, pg)
@@ -169,70 +57,40 @@ func (c *Client) GetFavourites(ctx context.Context, pg *Pagination) ([]*Status,
return statuses, nil
}
-// GetBookmarks returns the bookmark list of the current user.
-func (c *Client) GetBookmarks(ctx context.Context, pg *Pagination) ([]*Status, error) {
- var statuses []*Status
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/bookmarks", nil, &statuses, pg)
- if err != nil {
- return nil, err
- }
- return statuses, nil
-}
-
-// GetStatus returns status specified by id.
-func (c *Client) GetStatus(ctx context.Context, id ID) (*Status, error) {
+// GetStatus return status specified by id.
+func (c *Client) GetStatus(ctx context.Context, id int64) (*Status, error) {
var status Status
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s", id), nil, &status, nil)
+ err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d", id), nil, &status, nil)
if err != nil {
return nil, err
}
return &status, nil
}
-// GetStatusContext returns status specified by id.
-func (c *Client) GetStatusContext(ctx context.Context, id ID) (*Context, error) {
+// GetStatusContext return status specified by id.
+func (c *Client) GetStatusContext(ctx context.Context, id int64) (*Context, error) {
var context Context
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/context", id), nil, &context, nil)
+ err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/context", id), nil, &context, nil)
if err != nil {
return nil, err
}
return &context, nil
}
-// GetStatusCard returns status specified by id.
-func (c *Client) GetStatusCard(ctx context.Context, id ID) (*Card, error) {
+// GetStatusCard return status specified by id.
+func (c *Client) GetStatusCard(ctx context.Context, id int64) (*Card, error) {
var card Card
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/card", id), nil, &card, nil)
+ err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/card", id), nil, &card, nil)
if err != nil {
return nil, err
}
return &card, nil
}
-// GetStatusSource returns source data specified by id.
-func (c *Client) GetStatusSource(ctx context.Context, id ID) (*Source, error) {
- var source Source
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/source", id), nil, &source, nil)
- if err != nil {
- return nil, err
- }
- return &source, nil
-}
-
-// GetStatusHistory returns the status history specified by id.
-func (c *Client) GetStatusHistory(ctx context.Context, id ID) ([]*StatusHistory, error) {
- var statuses []*StatusHistory
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/history", id), nil, &statuses, nil)
- if err != nil {
- return nil, err
- }
- return statuses, nil
-}
-
// GetRebloggedBy returns the account list of the user who reblogged the toot of id.
-func (c *Client) GetRebloggedBy(ctx context.Context, id ID, pg *Pagination) ([]*Account, error) {
+func (c *Client) GetRebloggedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) {
var accounts []*Account
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/reblogged_by", id), nil, &accounts, pg)
+ err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts, pg)
if err != nil {
return nil, err
}
@@ -240,69 +98,49 @@ func (c *Client) GetRebloggedBy(ctx context.Context, id ID, pg *Pagination) ([]*
}
// GetFavouritedBy returns the account list of the user who liked the toot of id.
-func (c *Client) GetFavouritedBy(ctx context.Context, id ID, pg *Pagination) ([]*Account, error) {
+func (c *Client) GetFavouritedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) {
var accounts []*Account
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/favourited_by", id), nil, &accounts, pg)
+ err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts, pg)
if err != nil {
return nil, err
}
return accounts, nil
}
-// Reblog reblogs the toot of id and returns status of reblog.
-func (c *Client) Reblog(ctx context.Context, id ID) (*Status, error) {
+// Reblog is reblog the toot of id and return status of reblog.
+func (c *Client) Reblog(ctx context.Context, id int64) (*Status, error) {
var status Status
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/reblog", id), nil, &status, nil)
+ err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/reblog", id), nil, &status, nil)
if err != nil {
return nil, err
}
return &status, nil
}
-// Unreblog unreblogs the toot of id and returns status of the original toot.
-func (c *Client) Unreblog(ctx context.Context, id ID) (*Status, error) {
+// Unreblog is unreblog the toot of id and return status of the original toot.
+func (c *Client) Unreblog(ctx context.Context, id int64) (*Status, error) {
var status Status
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unreblog", id), nil, &status, nil)
+ err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unreblog", id), nil, &status, nil)
if err != nil {
return nil, err
}
return &status, nil
}
-// Favourite favourites the toot of id and returns status of the favourite toot.
-func (c *Client) Favourite(ctx context.Context, id ID) (*Status, error) {
+// Favourite is favourite the toot of id and return status of the favourite toot.
+func (c *Client) Favourite(ctx context.Context, id int64) (*Status, error) {
var status Status
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/favourite", id), nil, &status, nil)
+ err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/favourite", id), nil, &status, nil)
if err != nil {
return nil, err
}
return &status, nil
}
-// Unfavourite unfavourites the toot of id and returns status of the unfavourite toot.
-func (c *Client) Unfavourite(ctx context.Context, id ID) (*Status, error) {
+// Unfavourite is unfavourite the toot of id and return status of the unfavourite toot.
+func (c *Client) Unfavourite(ctx context.Context, id int64) (*Status, error) {
var status Status
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unfavourite", id), nil, &status, nil)
- if err != nil {
- return nil, err
- }
- return &status, nil
-}
-
-// Bookmark bookmarks the toot of id and returns status of the bookmark toot.
-func (c *Client) Bookmark(ctx context.Context, id ID) (*Status, error) {
- var status Status
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/bookmark", id), nil, &status, nil)
- if err != nil {
- return nil, err
- }
- return &status, nil
-}
-
-// Unbookmark is unbookmark the toot of id and return status of the unbookmark toot.
-func (c *Client) Unbookmark(ctx context.Context, id ID) (*Status, error) {
- var status Status
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unbookmark", id), nil, &status, nil)
+ err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unfavourite", id), nil, &status, nil)
if err != nil {
return nil, err
}
@@ -349,16 +187,6 @@ func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal boo
return statuses, nil
}
-// GetTimelineList return statuses from a list timeline.
-func (c *Client) GetTimelineList(ctx context.Context, id ID, pg *Pagination) ([]*Status, error) {
- var statuses []*Status
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/list/%s", url.PathEscape(string(id))), nil, &statuses, pg)
- if err != nil {
- return nil, err
- }
- return statuses, nil
-}
-
// GetTimelineMedia return statuses from media timeline.
// NOTE: This is an experimental feature of pawoo.net.
func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, error) {
@@ -378,58 +206,28 @@ func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool, pg *Paginat
// PostStatus post the toot.
func (c *Client) PostStatus(ctx context.Context, toot *Toot) (*Status, error) {
- return c.postStatus(ctx, toot, false, ID("none"))
-}
-
-// UpdateStatus updates the toot.
-func (c *Client) UpdateStatus(ctx context.Context, toot *Toot, id ID) (*Status, error) {
- return c.postStatus(ctx, toot, true, id)
-}
-
-func (c *Client) postStatus(ctx context.Context, toot *Toot, update bool, updateID ID) (*Status, error) {
params := url.Values{}
params.Set("status", toot.Status)
- if toot.InReplyToID != "" {
- params.Set("in_reply_to_id", string(toot.InReplyToID))
+ if toot.InReplyToID > 0 {
+ params.Set("in_reply_to_id", fmt.Sprint(toot.InReplyToID))
}
if toot.MediaIDs != nil {
for _, media := range toot.MediaIDs {
- params.Add("media_ids[]", string(media))
- }
- }
- // Can't use Media and Poll at the same time.
- if toot.Poll != nil && toot.Poll.Options != nil && toot.MediaIDs == nil {
- for _, opt := range toot.Poll.Options {
- params.Add("poll[options][]", string(opt))
- }
- params.Add("poll[expires_in]", fmt.Sprintf("%d", toot.Poll.ExpiresInSeconds))
- if toot.Poll.Multiple {
- params.Add("poll[multiple]", "true")
- }
- if toot.Poll.HideTotals {
- params.Add("poll[hide_totals]", "true")
+ params.Add("media_ids[]", strconv.FormatInt(media, 10))
}
}
if toot.Visibility != "" {
params.Set("visibility", fmt.Sprint(toot.Visibility))
}
- if toot.Language != "" {
- params.Set("language", fmt.Sprint(toot.Language))
- }
if toot.Sensitive {
- params.Set("sensitive", "true")
+ params.Set("senstitive", "true")
}
if toot.SpoilerText != "" {
params.Set("spoiler_text", toot.SpoilerText)
}
var status Status
- var err error
- if !update {
- err = c.doAPI(ctx, http.MethodPost, "/api/v1/statuses", params, &status, nil)
- } else {
- err = c.doAPI(ctx, http.MethodPut, fmt.Sprintf("/api/v1/statuses/%s", updateID), params, &status, nil)
- }
+ err := c.doAPI(ctx, http.MethodPost, "/api/v1/statuses", params, &status, nil)
if err != nil {
return nil, err
}
@@ -437,8 +235,8 @@ func (c *Client) postStatus(ctx context.Context, toot *Toot, update bool, update
}
// DeleteStatus delete the toot.
-func (c *Client) DeleteStatus(ctx context.Context, id ID) error {
- return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%s", id), nil, nil, nil)
+func (c *Client) DeleteStatus(ctx context.Context, id int64) error {
+ return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%d", id), nil, nil, nil)
}
// Search search content with query.
@@ -447,81 +245,19 @@ func (c *Client) Search(ctx context.Context, q string, resolve bool) (*Results,
params.Set("q", q)
params.Set("resolve", fmt.Sprint(resolve))
var results Results
- err := c.doAPI(ctx, http.MethodGet, "/api/v2/search", params, &results, nil)
+ err := c.doAPI(ctx, http.MethodGet, "/api/v1/search", params, &results, nil)
if err != nil {
return nil, err
}
return &results, nil
}
-// UploadMedia upload a media attachment from a file.
+// UploadMedia upload a media attachment.
func (c *Client) UploadMedia(ctx context.Context, file string) (*Attachment, error) {
- f, err := os.Open(file)
- if err != nil {
- return nil, err
- }
- defer f.Close()
-
- return c.UploadMediaFromMedia(ctx, &Media{File: f})
-}
-
-// UploadMediaFromBytes uploads a media attachment from a byte slice.
-func (c *Client) UploadMediaFromBytes(ctx context.Context, b []byte) (*Attachment, error) {
- return c.UploadMediaFromReader(ctx, bytes.NewReader(b))
-}
-
-// UploadMediaFromReader uploads a media attachment from an io.Reader.
-func (c *Client) UploadMediaFromReader(ctx context.Context, reader io.Reader) (*Attachment, error) {
- return c.UploadMediaFromMedia(ctx, &Media{File: reader})
-}
-
-// UploadMediaFromMedia uploads a media attachment from a Media struct.
-func (c *Client) UploadMediaFromMedia(ctx context.Context, media *Media) (*Attachment, error) {
var attachment Attachment
- if err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", media, &attachment, nil); err != nil {
+ err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", file, &attachment, nil)
+ if err != nil {
return nil, err
}
return &attachment, nil
}
-
-// GetTimelineDirect return statuses from direct timeline.
-func (c *Client) GetTimelineDirect(ctx context.Context, pg *Pagination) ([]*Status, error) {
- params := url.Values{}
-
- var conversations []*Conversation
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/conversations", params, &conversations, pg)
- if err != nil {
- return nil, err
- }
-
- var statuses = []*Status{}
-
- for _, c := range conversations {
- s := c.LastStatus
- statuses = append(statuses, s)
- }
-
- return statuses, nil
-}
-
-// GetConversations return direct conversations.
-func (c *Client) GetConversations(ctx context.Context, pg *Pagination) ([]*Conversation, error) {
- params := url.Values{}
-
- var conversations []*Conversation
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/conversations", params, &conversations, pg)
- if err != nil {
- return nil, err
- }
- return conversations, nil
-}
-
-// DeleteConversation delete the conversation specified by id.
-func (c *Client) DeleteConversation(ctx context.Context, id ID) error {
- return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/conversations/%s", id), nil, nil, nil)
-}
-
-// MarkConversationAsRead mark the conversation as read.
-func (c *Client) MarkConversationAsRead(ctx context.Context, id ID) error {
- return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/conversations/%s/read", id), nil, nil, nil)
-}
diff --git a/status_test.go b/status_test.go
index 2cac4b8..1359bb9 100644
--- a/status_test.go
+++ b/status_test.go
@@ -3,16 +3,15 @@ package mastodon
import (
"context"
"fmt"
- "io/ioutil"
"net/http"
"net/http/httptest"
- "os"
"testing"
)
func TestGetFavourites(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `[{"content": "foo"}, {"content": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -37,40 +36,14 @@ func TestGetFavourites(t *testing.T) {
}
}
-func TestGetBookmarks(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintln(w, `[{"content": "foo"}, {"content": "bar"}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- books, err := client.GetBookmarks(context.Background(), nil)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(books) != 2 {
- t.Fatalf("result should be two: %d", len(books))
- }
- if books[0].Content != "foo" {
- t.Fatalf("want %q but %q", "foo", books[0].Content)
- }
- if books[1].Content != "bar" {
- t.Fatalf("want %q but %q", "bar", books[1].Content)
- }
-}
-
func TestGetStatus(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/v1/statuses/1234567" {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
- fmt.Fprintln(w, `{"content": "zzz", "emojis":[{"shortcode":"💩", "url":"http://example.com", "static_url": "http://example.com/static"}]}`)
+ fmt.Fprintln(w, `{"content": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -80,29 +53,17 @@ func TestGetStatus(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.GetStatus(context.Background(), "123")
+ _, err := client.GetStatus(context.Background(), 123)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- status, err := client.GetStatus(context.Background(), "1234567")
+ status, err := client.GetStatus(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
if status.Content != "zzz" {
t.Fatalf("want %q but %q", "zzz", status.Content)
}
- if len(status.Emojis) != 1 {
- t.Fatal("should have emojis")
- }
- if status.Emojis[0].ShortCode != "💩" {
- t.Fatalf("want %q but %q", "💩", status.Emojis[0].ShortCode)
- }
- if status.Emojis[0].URL != "http://example.com" {
- t.Fatalf("want %q but %q", "https://example.com", status.Emojis[0].URL)
- }
- if status.Emojis[0].StaticURL != "http://example.com/static" {
- t.Fatalf("want %q but %q", "https://example.com/static", status.Emojis[0].StaticURL)
- }
}
func TestGetStatusCard(t *testing.T) {
@@ -112,6 +73,7 @@ func TestGetStatusCard(t *testing.T) {
return
}
fmt.Fprintln(w, `{"title": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -121,11 +83,11 @@ func TestGetStatusCard(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.GetStatusCard(context.Background(), "123")
+ _, err := client.GetStatusCard(context.Background(), 123)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- card, err := client.GetStatusCard(context.Background(), "1234567")
+ card, err := client.GetStatusCard(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -141,6 +103,7 @@ func TestGetStatusContext(t *testing.T) {
return
}
fmt.Fprintln(w, `{"ancestors": [{"content": "zzz"},{"content": "bbb"}]}`)
+ return
}))
defer ts.Close()
@@ -150,11 +113,11 @@ func TestGetStatusContext(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.GetStatusContext(context.Background(), "123")
+ _, err := client.GetStatusContext(context.Background(), 123)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- context, err := client.GetStatusContext(context.Background(), "1234567")
+ context, err := client.GetStatusContext(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -172,88 +135,6 @@ func TestGetStatusContext(t *testing.T) {
}
}
-func TestGetStatusSource(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/statuses/1234567/source" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `{"id":"1234567","text":"Foo","spoiler_text":"Bar"}%`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetStatusSource(context.Background(), "123")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- source, err := client.GetStatusSource(context.Background(), "1234567")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if source.ID != ID("1234567") {
- t.Fatalf("want %q but %q", "1234567", source.ID)
- }
- if source.Text != "Foo" {
- t.Fatalf("want %q but %q", "Foo", source.Text)
- }
- if source.SpoilerText != "Bar" {
- t.Fatalf("want %q but %q", "Bar", source.SpoilerText)
- }
-}
-
-func TestGetStatusHistory(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/statuses/1234567/history" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `[{"content": "foo", "emojis":[{"shortcode":"💩", "url":"http://example.com", "static_url": "http://example.com/static"}]}, {"content": "bar", "emojis":[{"shortcode":"💩", "url":"http://example.com", "static_url": "http://example.com/static"}]}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetStatusHistory(context.Background(), "123")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- statuses, err := client.GetStatusHistory(context.Background(), "1234567")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(statuses) != 2 {
- t.Fatalf("want len %q but got %q", "2", len(statuses))
- }
- if statuses[0].Content != "foo" {
- t.Fatalf("want %q but %q", "bar", statuses[0].Content)
- }
- if statuses[1].Content != "bar" {
- t.Fatalf("want %q but %q", "bar", statuses[1].Content)
- }
- if len(statuses[0].Emojis) != 1 {
- t.Fatal("should have emojis")
- }
- if statuses[0].Emojis[0].ShortCode != "💩" {
- t.Fatalf("want %q but %q", "💩", statuses[0].Emojis[0].ShortCode)
- }
- if statuses[0].Emojis[0].URL != "http://example.com" {
- t.Fatalf("want %q but %q", "https://example.com", statuses[0].Emojis[0].URL)
- }
- if statuses[0].Emojis[0].StaticURL != "http://example.com/static" {
- t.Fatalf("want %q but %q", "https://example.com/static", statuses[0].Emojis[0].StaticURL)
- }
-}
-
func TestGetRebloggedBy(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/v1/statuses/1234567/reblogged_by" {
@@ -261,6 +142,7 @@ func TestGetRebloggedBy(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -270,11 +152,11 @@ func TestGetRebloggedBy(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.GetRebloggedBy(context.Background(), "123", nil)
+ _, err := client.GetRebloggedBy(context.Background(), 123, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- rbs, err := client.GetRebloggedBy(context.Background(), "1234567", nil)
+ rbs, err := client.GetRebloggedBy(context.Background(), 1234567, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -296,6 +178,7 @@ func TestGetFavouritedBy(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`)
+ return
}))
defer ts.Close()
@@ -305,11 +188,11 @@ func TestGetFavouritedBy(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.GetFavouritedBy(context.Background(), "123", nil)
+ _, err := client.GetFavouritedBy(context.Background(), 123, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- fbs, err := client.GetFavouritedBy(context.Background(), "1234567", nil)
+ fbs, err := client.GetFavouritedBy(context.Background(), 1234567, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -331,6 +214,7 @@ func TestReblog(t *testing.T) {
return
}
fmt.Fprintln(w, `{"content": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -340,11 +224,11 @@ func TestReblog(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.Reblog(context.Background(), "123")
+ _, err := client.Reblog(context.Background(), 123)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- status, err := client.Reblog(context.Background(), "1234567")
+ status, err := client.Reblog(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -360,6 +244,7 @@ func TestUnreblog(t *testing.T) {
return
}
fmt.Fprintln(w, `{"content": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -369,11 +254,11 @@ func TestUnreblog(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.Unreblog(context.Background(), "123")
+ _, err := client.Unreblog(context.Background(), 123)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- status, err := client.Unreblog(context.Background(), "1234567")
+ status, err := client.Unreblog(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -389,6 +274,7 @@ func TestFavourite(t *testing.T) {
return
}
fmt.Fprintln(w, `{"content": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -398,11 +284,11 @@ func TestFavourite(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.Favourite(context.Background(), "123")
+ _, err := client.Favourite(context.Background(), 123)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- status, err := client.Favourite(context.Background(), "1234567")
+ status, err := client.Favourite(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -418,6 +304,7 @@ func TestUnfavourite(t *testing.T) {
return
}
fmt.Fprintln(w, `{"content": "zzz"}`)
+ return
}))
defer ts.Close()
@@ -427,69 +314,11 @@ func TestUnfavourite(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- _, err := client.Unfavourite(context.Background(), "123")
+ _, err := client.Unfavourite(context.Background(), 123)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- status, err := client.Unfavourite(context.Background(), "1234567")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if status.Content != "zzz" {
- t.Fatalf("want %q but %q", "zzz", status.Content)
- }
-}
-
-func TestBookmark(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/statuses/1234567/bookmark" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `{"content": "zzz"}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.Bookmark(context.Background(), "123")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- status, err := client.Bookmark(context.Background(), "1234567")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if status.Content != "zzz" {
- t.Fatalf("want %q but %q", "zzz", status.Content)
- }
-}
-
-func TestUnbookmark(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/statuses/1234567/unbookmark" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `{"content": "zzz"}`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.Unbookmark(context.Background(), "123")
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- status, err := client.Unbookmark(context.Background(), "1234567")
+ status, err := client.Unfavourite(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -528,28 +357,6 @@ func TestGetTimelinePublic(t *testing.T) {
}
}
-func TestGetTimelineDirect(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintln(w, `[{"id": "4", "unread":false, "last_status" : {"content": "zzz"}}, {"id": "3", "unread":true, "last_status" : {"content": "bar"}}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{Server: ts.URL})
- tl, err := client.GetTimelineDirect(context.Background(), nil)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(tl) != 2 {
- t.Fatalf("result should be two: %d", len(tl))
- }
- if tl[0].Content != "zzz" {
- t.Fatalf("want %q but %q", "foo", tl[0].Content)
- }
- if tl[1].Content != "bar" {
- t.Fatalf("want %q but %q", "bar", tl[1].Content)
- }
-}
-
func TestGetTimelineHashtag(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/v1/timelines/tag/zzz" {
@@ -557,6 +364,7 @@ func TestGetTimelineHashtag(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"content": "zzz"},{"content": "yyy"}]`)
+ return
}))
defer ts.Close()
@@ -585,41 +393,6 @@ func TestGetTimelineHashtag(t *testing.T) {
}
}
-func TestGetTimelineList(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/timelines/list/1" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- fmt.Fprintln(w, `[{"content": "zzz"},{"content": "yyy"}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- _, err := client.GetTimelineList(context.Background(), "notfound", nil)
- if err == nil {
- t.Fatalf("should be fail: %v", err)
- }
- tags, err := client.GetTimelineList(context.Background(), "1", nil)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(tags) != 2 {
- t.Fatalf("should have %q entries but %q", "2", len(tags))
- }
- if tags[0].Content != "zzz" {
- t.Fatalf("want %q but %q", "zzz", tags[0].Content)
- }
- if tags[1].Content != "yyy" {
- t.Fatalf("want %q but %q", "zzz", tags[1].Content)
- }
-}
-
func TestGetTimelineMedia(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("local") == "" {
@@ -627,6 +400,7 @@ func TestGetTimelineMedia(t *testing.T) {
return
}
fmt.Fprintln(w, `[{"content": "zzz"},{"content": "yyy"}]`)
+ return
}))
defer ts.Close()
@@ -665,6 +439,7 @@ func TestDeleteStatus(t *testing.T) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusMethodNotAllowed)
return
}
+ return
}))
defer ts.Close()
@@ -674,11 +449,11 @@ func TestDeleteStatus(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
- err := client.DeleteStatus(context.Background(), "123")
+ err := client.DeleteStatus(context.Background(), 123)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
- err = client.DeleteStatus(context.Background(), "1234567")
+ err = client.DeleteStatus(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@@ -686,11 +461,11 @@ func TestDeleteStatus(t *testing.T) {
func TestSearch(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v2/search" {
+ if r.URL.Path != "/api/v1/search" {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
- if r.RequestURI != "/api/v2/search?q=q&resolve=false" {
+ if r.RequestURI != "/api/v1/search?q=q&resolve=false" {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusBadRequest)
return
}
@@ -698,8 +473,9 @@ func TestSearch(t *testing.T) {
fmt.Fprintln(w, `
{"accounts":[{"username": "zzz"},{"username": "yyy"}],
"statuses":[{"content": "aaa"}],
- "hashtags":[{"name": "tag"},{"name": "tag2"},{"name": "tag3"}]
+ "hashtags":["tag","tag2","tag3"]
}`)
+ return
}))
defer ts.Close()
@@ -728,7 +504,7 @@ func TestSearch(t *testing.T) {
if len(ret.Hashtags) != 3 {
t.Fatalf("Hashtags have %q entries, but %q", "3", len(ret.Hashtags))
}
- if ret.Hashtags[2].Name != "tag3" {
+ if ret.Hashtags[2] != "tag3" {
t.Fatalf("Hashtags[2] should %q , but %q", "tag3", ret.Hashtags[2])
}
}
@@ -744,6 +520,7 @@ func TestUploadMedia(t *testing.T) {
return
}
fmt.Fprintln(w, `{"id": 123}`)
+ return
}))
defer ts.Close()
@@ -757,114 +534,7 @@ func TestUploadMedia(t *testing.T) {
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- if attachment.ID != "123" {
- t.Fatalf("want %q but %q", "123", attachment.ID)
- }
- file, err := os.Open("testdata/logo.png")
- if err != nil {
- t.Fatalf("could not open file: %v", err)
- }
- defer file.Close()
- writerAttachment, err := client.UploadMediaFromReader(context.Background(), file)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if writerAttachment.ID != "123" {
- t.Fatalf("want %q but %q", "123", attachment.ID)
- }
- bytes, err := ioutil.ReadFile("testdata/logo.png")
- if err != nil {
- t.Fatalf("could not open file: %v", err)
- }
- byteAttachment, err := client.UploadMediaFromBytes(context.Background(), bytes)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if byteAttachment.ID != "123" {
- t.Fatalf("want %q but got %q", "123", attachment.ID)
- }
-
-}
-
-func TestGetConversations(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/conversations" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- }
- fmt.Fprintln(w, `[{"id": "4", "unread":false, "last_status" : {"content": "zzz"}}, {"id": "3", "unread":true, "last_status" : {"content": "bar"}}]`)
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- convs, err := client.GetConversations(context.Background(), nil)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- if len(convs) != 2 {
- t.Fatalf("result should be 2: %d", len(convs))
- }
- if convs[0].ID != "4" {
- t.Fatalf("want %q but %q", "4", convs[0].ID)
- }
- if convs[0].LastStatus.Content != "zzz" {
- t.Fatalf("want %q but %q", "zzz", convs[0].LastStatus.Content)
- }
- if convs[1].Unread != true {
- t.Fatalf("unread should be true: %t", convs[1].Unread)
- }
-}
-
-func TestDeleteConversation(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/conversations/12345678" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- if r.Method != "DELETE" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusMethodNotAllowed)
- return
- }
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "hoge",
- })
- err := client.DeleteConversation(context.Background(), "12345678")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
-}
-
-func TestMarkConversationsAsRead(t *testing.T) {
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/api/v1/conversations/111111/read" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- if r.Method != "POST" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- }))
- defer ts.Close()
-
- client := NewClient(&Config{
- Server: ts.URL,
- ClientID: "foo",
- ClientSecret: "bar",
- AccessToken: "zoo",
- })
- err := client.MarkConversationAsRead(context.Background(), "111111")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
+ if attachment.ID != 123 {
+ t.Fatalf("want %q but %q", 123, attachment.ID)
}
}
diff --git a/streaming.go b/streaming.go
index b109f6c..0d3e559 100644
--- a/streaming.go
+++ b/streaming.go
@@ -2,77 +2,52 @@ package mastodon
import (
"bufio"
- "bytes"
"context"
"encoding/json"
- "errors"
"io"
"net/http"
"net/url"
"path"
+ "strconv"
"strings"
)
-// UpdateEvent is a struct for passing status event to app.
+// UpdateEvent is struct for passing status event to app.
type UpdateEvent struct {
Status *Status `json:"status"`
}
func (e *UpdateEvent) event() {}
-// UpdateEditEvent is a struct for passing status edit event to app.
-type UpdateEditEvent struct {
- Status *Status `json:"status"`
-}
-
-func (e *UpdateEditEvent) event() {}
-
-// NotificationEvent is a struct for passing notification event to app.
+// NotificationEvent is struct for passing notification event to app.
type NotificationEvent struct {
Notification *Notification `json:"notification"`
}
func (e *NotificationEvent) event() {}
-// DeleteEvent is a struct for passing deletion event to app.
-type DeleteEvent struct{ ID ID }
+// DeleteEvent is struct for passing deletion event to app.
+type DeleteEvent struct{ ID int64 }
func (e *DeleteEvent) event() {}
-// ErrorEvent is a struct for passing errors to app.
+// ErrorEvent is struct for passing errors to app.
type ErrorEvent struct{ err error }
func (e *ErrorEvent) event() {}
func (e *ErrorEvent) Error() string { return e.err.Error() }
-// Event is an interface passing events to app.
+// Event is interface passing events to app.
type Event interface {
event()
}
func handleReader(q chan Event, r io.Reader) error {
var name string
- var lineBuf bytes.Buffer
- br := bufio.NewReader(r)
- for {
- line, isPrefix, err := br.ReadLine()
- if err != nil {
- if errors.Is(err, io.EOF) {
- return nil
- }
- return err
- }
- if isPrefix {
- lineBuf.Write(line)
- continue
- }
- if lineBuf.Len() > 0 {
- lineBuf.Write(line)
- line = lineBuf.Bytes()
- lineBuf.Reset()
- }
-
- token := strings.SplitN(string(line), ":", 2)
+ s := bufio.NewScanner(r)
+ for s.Scan() {
+ line := s.Text()
+ token := strings.SplitN(line, ":", 2)
if len(token) != 2 {
continue
}
@@ -88,12 +63,6 @@ func handleReader(q chan Event, r io.Reader) error {
if err == nil {
q <- &UpdateEvent{&status}
}
- case "status.update":
- var status Status
- err = json.Unmarshal([]byte(token[1]), &status)
- if err == nil {
- q <- &UpdateEditEvent{&status}
- }
case "notification":
var notification Notification
err = json.Unmarshal([]byte(token[1]), ¬ification)
@@ -101,17 +70,22 @@ func handleReader(q chan Event, r io.Reader) error {
q <- &NotificationEvent{¬ification}
}
case "delete":
- q <- &DeleteEvent{ID: ID(strings.TrimSpace(token[1]))}
+ var id int64
+ id, err = strconv.ParseInt(strings.TrimSpace(token[1]), 10, 64)
+ if err == nil {
+ q <- &DeleteEvent{id}
+ }
}
if err != nil {
q <- &ErrorEvent{err}
}
}
}
+ return s.Err()
}
func (c *Client) streaming(ctx context.Context, p string, params url.Values) (chan Event, error) {
- u, err := url.Parse(c.Config.Server)
+ u, err := url.Parse(c.config.Server)
if err != nil {
return nil, err
}
@@ -123,10 +97,7 @@ func (c *Client) streaming(ctx context.Context, p string, params url.Values) (ch
return nil, err
}
req = req.WithContext(ctx)
-
- if c.Config.AccessToken != "" {
- req.Header.Set("Authorization", "Bearer "+c.Config.AccessToken)
- }
+ req.Header.Set("Authorization", "Bearer "+c.config.AccessToken)
q := make(chan Event)
go func() {
@@ -163,12 +134,12 @@ func (c *Client) doStreaming(req *http.Request, q chan Event) {
}
}
-// StreamingUser returns a channel to read events on home.
+// StreamingUser return channel to read events on home.
func (c *Client) StreamingUser(ctx context.Context) (chan Event, error) {
return c.streaming(ctx, "user", nil)
}
-// StreamingPublic returns a channel to read events on public.
+// StreamingPublic return channel to read events on public.
func (c *Client) StreamingPublic(ctx context.Context, isLocal bool) (chan Event, error) {
p := "public"
if isLocal {
@@ -178,7 +149,7 @@ func (c *Client) StreamingPublic(ctx context.Context, isLocal bool) (chan Event,
return c.streaming(ctx, p, nil)
}
-// StreamingHashtag returns a channel to read events on tagged timeline.
+// StreamingHashtag return channel to read events on tagged timeline.
func (c *Client) StreamingHashtag(ctx context.Context, tag string, isLocal bool) (chan Event, error) {
params := url.Values{}
params.Set("tag", tag)
@@ -190,16 +161,3 @@ func (c *Client) StreamingHashtag(ctx context.Context, tag string, isLocal bool)
return c.streaming(ctx, p, params)
}
-
-// StreamingList returns a channel to read events on a list.
-func (c *Client) StreamingList(ctx context.Context, id ID) (chan Event, error) {
- params := url.Values{}
- params.Set("list", string(id))
-
- return c.streaming(ctx, "list", params)
-}
-
-// StreamingDirect returns a channel to read events on a direct messages.
-func (c *Client) StreamingDirect(ctx context.Context) (chan Event, error) {
- return c.streaming(ctx, "direct", nil)
-}
diff --git a/streaming_test.go b/streaming_test.go
index a9e90d9..9f5b715 100644
--- a/streaming_test.go
+++ b/streaming_test.go
@@ -1,65 +1,42 @@
package mastodon
import (
- "bufio"
"context"
"fmt"
"net/http"
"net/http/httptest"
"strings"
- "sync"
"testing"
"time"
)
func TestHandleReader(t *testing.T) {
- large := "large"
- largeContent := strings.Repeat(large, 2*(bufio.MaxScanTokenSize/len(large)))
-
q := make(chan Event)
- r := strings.NewReader(fmt.Sprintf(`
+ r := strings.NewReader(`
event: update
data: {content: error}
event: update
data: {"content": "foo"}
-event: update
-data: {"content": "%s"}
event: notification
data: {"type": "mention"}
event: delete
data: 1234567
-event: status.update
-data: {"content": "foo"}
:thump
- `, largeContent))
- var wg sync.WaitGroup
- wg.Add(1)
+ `)
go func() {
- defer wg.Done()
defer close(q)
err := handleReader(q, r)
if err != nil {
- t.Errorf("should not be fail: %v", err)
+ t.Fatalf("should not be fail: %v", err)
}
}()
- var passUpdate, passUpdateLarge, passNotification, passDelete, passError bool
+ var passUpdate, passNotification, passDelete, passError bool
for e := range q {
switch event := e.(type) {
case *UpdateEvent:
- if event.Status.Content == "foo" {
- passUpdate = true
- } else if event.Status.Content == largeContent {
- passUpdateLarge = true
- } else {
- t.Fatalf("bad update content: %q", event.Status.Content)
- }
- case *UpdateEditEvent:
- if event.Status.Content == "foo" {
- passUpdate = true
- } else if event.Status.Content == largeContent {
- passUpdateLarge = true
- } else {
- t.Fatalf("bad update content: %q", event.Status.Content)
+ passUpdate = true
+ if event.Status.Content != "foo" {
+ t.Fatalf("want %q but %q", "foo", event.Status.Content)
}
case *NotificationEvent:
passNotification = true
@@ -68,8 +45,8 @@ data: {"content": "foo"}
}
case *DeleteEvent:
passDelete = true
- if event.ID != "1234567" {
- t.Fatalf("want %q but %q", "1234567", event.ID)
+ if event.ID != 1234567 {
+ t.Fatalf("want %d but %d", 1234567, event.ID)
}
case *ErrorEvent:
passError = true
@@ -78,12 +55,11 @@ data: {"content": "foo"}
}
}
}
- if !passUpdate || !passUpdateLarge || !passNotification || !passDelete || !passError {
+ if !passUpdate || !passNotification || !passDelete || !passError {
t.Fatalf("have not passed through somewhere: "+
- "update: %t, update (large): %t, notification: %t, delete: %t, error: %t",
- passUpdate, passUpdateLarge, passNotification, passDelete, passError)
+ "update %t, notification %t, delete %t, error %t",
+ passUpdate, passNotification, passDelete, passError)
}
- wg.Wait()
}
func TestStreaming(t *testing.T) {
@@ -135,12 +111,6 @@ data: {"content": "foo"}
if event.Status.Content != "foo" {
t.Fatalf("want %q but %q", "foo", event.Status.Content)
}
- case *UpdateEditEvent:
- cnt++
- passUpdate = true
- if event.Status.Content != "foo" {
- t.Fatalf("want %q but %q", "foo", event.Status.Content)
- }
}
}
if cnt != 1 {
@@ -169,14 +139,11 @@ func TestDoStreaming(t *testing.T) {
req = req.WithContext(ctx)
q := make(chan Event)
- var wg sync.WaitGroup
- wg.Add(1)
go func() {
- defer wg.Done()
defer close(q)
c.doStreaming(req, q)
if err != nil {
- t.Errorf("should not be fail: %v", err)
+ t.Fatalf("should not be fail: %v", err)
}
}()
var passError bool
@@ -191,7 +158,6 @@ func TestDoStreaming(t *testing.T) {
if !passError {
t.Fatalf("have not passed through: error %t", passError)
}
- wg.Wait()
}
func TestStreamingUser(t *testing.T) {
@@ -327,83 +293,3 @@ data: {"content": "foo"}
t.Fatalf("want %q but %q", "foo", events[0].(*UpdateEvent).Status.Content)
}
}
-
-func TestStreamingList(t *testing.T) {
- var isEnd bool
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if isEnd {
- return
- } else if r.URL.Path != "/api/v1/streaming/list" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- f, _ := w.(http.Flusher)
- fmt.Fprintln(w, `
-event: update
-data: {"content": "foo"}
- `)
- f.Flush()
- isEnd = true
- }))
- defer ts.Close()
-
- client := NewClient(&Config{Server: ts.URL})
- ctx, cancel := context.WithCancel(context.Background())
- time.AfterFunc(time.Second, cancel)
- q, err := client.StreamingList(ctx, "1")
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- events := []Event{}
- for e := range q {
- if _, ok := e.(*ErrorEvent); !ok {
- events = append(events, e)
- }
- }
- if len(events) != 1 {
- t.Fatalf("result should be one: %d", len(events))
- }
- if events[0].(*UpdateEvent).Status.Content != "foo" {
- t.Fatalf("want %q but %q", "foo", events[0].(*UpdateEvent).Status.Content)
- }
-}
-
-func TestStreamingDirect(t *testing.T) {
- var isEnd bool
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if isEnd {
- return
- } else if r.URL.Path != "/api/v1/streaming/direct" {
- http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
- return
- }
- f, _ := w.(http.Flusher)
- fmt.Fprintln(w, `
-event: update
-data: {"content": "foo"}
- `)
- f.Flush()
- isEnd = true
- }))
- defer ts.Close()
-
- client := NewClient(&Config{Server: ts.URL})
- ctx, cancel := context.WithCancel(context.Background())
- time.AfterFunc(time.Second, cancel)
- q, err := client.StreamingDirect(ctx)
- if err != nil {
- t.Fatalf("should not be fail: %v", err)
- }
- events := []Event{}
- for e := range q {
- if _, ok := e.(*ErrorEvent); !ok {
- events = append(events, e)
- }
- }
- if len(events) != 1 {
- t.Fatalf("result should be one: %d", len(events))
- }
- if events[0].(*UpdateEvent).Status.Content != "foo" {
- t.Fatalf("want %q but %q", "foo", events[0].(*UpdateEvent).Status.Content)
- }
-}
diff --git a/streaming_ws.go b/streaming_ws.go
index 5658bbf..5553a25 100644
--- a/streaming_ws.go
+++ b/streaming_ws.go
@@ -3,10 +3,8 @@ package mastodon
import (
"context"
"encoding/json"
- "fmt"
"net/url"
"path"
- "strings"
"github.com/gorilla/websocket"
)
@@ -51,20 +49,15 @@ func (c *WSClient) StreamingWSHashtag(ctx context.Context, tag string, isLocal b
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("access_token", c.client.config.AccessToken)
params.Set("stream", stream)
if tag != "" {
params.Set("tag", tag)
}
- u, err := changeWebSocketScheme(c.client.Config.Server)
+ u, err := changeWebSocketScheme(c.client.config.Server)
if err != nil {
return nil, err
}
@@ -127,12 +120,6 @@ func (c *WSClient) handleWS(ctx context.Context, rawurl string, q chan Event) er
if err == nil {
q <- &UpdateEvent{Status: &status}
}
- case "status.update":
- var status Status
- err = json.Unmarshal([]byte(s.Payload.(string)), &status)
- if err == nil {
- q <- &UpdateEditEvent{Status: &status}
- }
case "notification":
var notification Notification
err = json.Unmarshal([]byte(s.Payload.(string)), ¬ification)
@@ -140,11 +127,7 @@ func (c *WSClient) handleWS(ctx context.Context, rawurl string, q chan Event) er
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)))}
- }
+ q <- &DeleteEvent{ID: int64(s.Payload.(float64))}
}
if err != nil {
q <- &ErrorEvent{err}
diff --git a/streaming_ws_test.go b/streaming_ws_test.go
index 7e78890..f52477c 100644
--- a/streaming_ws_test.go
+++ b/streaming_ws_test.go
@@ -4,7 +4,6 @@ import (
"context"
"net/http"
"net/http/httptest"
- "sync"
"testing"
"time"
@@ -80,13 +79,6 @@ func wsMock(w http.ResponseWriter, r *http.Request) {
return
}
- err = conn.WriteMessage(websocket.TextMessage,
- []byte(`{"event":"status.update","payload":"{\"content\":\"bar\"}"}`))
- 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 {
@@ -119,20 +111,20 @@ func wsTest(t *testing.T, q chan Event, cancel func()) {
for e := range q {
events = append(events, e)
}
- if len(events) != 7 {
- t.Fatalf("result should be seven: %d", len(events))
+ 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].(*UpdateEditEvent).Status.Content != "bar" {
- t.Fatalf("want %q but %q", "bar", events[1].(*UpdateEditEvent).Status.Content)
+ if events[1].(*NotificationEvent).Notification.ID != 123 {
+ t.Fatalf("want %d but %d", 123, events[1].(*NotificationEvent).Notification.ID)
}
- if events[2].(*NotificationEvent).Notification.ID != "123" {
- t.Fatalf("want %q but %q", "123", events[2].(*NotificationEvent).Notification.ID)
+ if events[2].(*DeleteEvent).ID != 1234567 {
+ t.Fatalf("want %d but %d", 1234567, events[2].(*DeleteEvent).ID)
}
- if events[3].(*DeleteEvent).ID != "1234567" {
- t.Fatalf("want %q but %q", "1234567", events[3].(*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)
@@ -140,9 +132,6 @@ func wsTest(t *testing.T, q chan Event, cancel func()) {
if errorEvent, ok := events[5].(*ErrorEvent); !ok {
t.Fatalf("should be fail: %v", errorEvent.err)
}
- if errorEvent, ok := events[6].(*ErrorEvent); !ok {
- t.Fatalf("should be fail: %v", errorEvent.err)
- }
}
func TestStreamingWS(t *testing.T) {
@@ -162,16 +151,12 @@ func TestStreamingWS(t *testing.T) {
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
- var wg sync.WaitGroup
- wg.Add(1)
go func() {
- defer wg.Done()
e := <-q
if errorEvent, ok := e.(*ErrorEvent); !ok {
- t.Errorf("should be fail: %v", errorEvent.err)
+ t.Fatalf("should be fail: %v", errorEvent.err)
}
}()
- wg.Wait()
}
func TestHandleWS(t *testing.T) {
@@ -198,13 +183,10 @@ func TestHandleWS(t *testing.T) {
q := make(chan Event)
client := NewClient(&Config{}).NewWSClient()
- var wg sync.WaitGroup
- wg.Add(1)
go func() {
- defer wg.Done()
e := <-q
if errorEvent, ok := e.(*ErrorEvent); !ok {
- t.Errorf("should be fail: %v", errorEvent.err)
+ t.Fatalf("should be fail: %v", errorEvent.err)
}
}()
err := client.handleWS(context.Background(), ":", q)
@@ -214,12 +196,10 @@ func TestHandleWS(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
- wg.Add(1)
go func() {
- defer wg.Done()
e := <-q
if errorEvent, ok := e.(*ErrorEvent); !ok {
- t.Errorf("should be fail: %v", errorEvent.err)
+ t.Fatalf("should be fail: %v", errorEvent.err)
}
}()
err = client.handleWS(ctx, "ws://"+ts.Listener.Addr().String(), q)
@@ -227,17 +207,13 @@ func TestHandleWS(t *testing.T) {
t.Fatalf("should be fail: %v", err)
}
- wg.Add(1)
go func() {
- defer wg.Done()
e := <-q
if errorEvent, ok := e.(*ErrorEvent); !ok {
- t.Errorf("should be fail: %v", errorEvent.err)
+ t.Fatalf("should be fail: %v", errorEvent.err)
}
}()
client.handleWS(context.Background(), "ws://"+ts.Listener.Addr().String(), q)
-
- wg.Wait()
}
func TestDialRedirect(t *testing.T) {
@@ -267,12 +243,12 @@ func TestDial(t *testing.T) {
t.Fatalf("should be fail: %v", err)
}
- _, _, err = client.dial("ws://" + ts.Listener.Addr().String())
+ _, rawurl, 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())
+ _, rawurl, err = client.dial("ws://" + ts.Listener.Addr().String())
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
diff --git a/unixtime.go b/unixtime.go
deleted file mode 100644
index a935a9e..0000000
--- a/unixtime.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package mastodon
-
-import (
- "strconv"
- "time"
-)
-
-type Unixtime time.Time
-
-func (t *Unixtime) UnmarshalJSON(data []byte) error {
- if len(data) > 0 && data[0] == '"' && data[len(data)-1] == '"' {
- data = data[1 : len(data)-1]
- }
- ts, err := strconv.ParseInt(string(data), 10, 64)
- if err != nil {
- return err
- }
- *t = Unixtime(time.Unix(ts, 0))
- return nil
-}