Skip to content
Open
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func main() {
* [x] GET /api/v1/follow_requests
* [x] POST /api/v1/follow_requests/:id/authorize
* [x] POST /api/v1/follow_requests/:id/reject
* [x] GET /api/v1/followed_tags
* [x] POST /api/v1/follows
* [x] GET /api/v1/instance
* [x] GET /api/v1/instance/activity
Expand Down Expand Up @@ -143,16 +144,19 @@ func main() {
* [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
* [x] GET /api/v1/tags/:hashtag
* [x] POST /api/v1/tags/:hashtag/follow
* [x] POST /api/v1/tags/:hashtag/unfollow
* [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

## Installation

Expand Down
14 changes: 14 additions & 0 deletions accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,20 @@ func (c *Client) AccountsSearch(ctx context.Context, q string, limit int64) ([]*
return accounts, nil
}

func (c *Client) AccountsSearchResolve(ctx context.Context, q string, limit int64, resolve bool) ([]*Account, error) {
params := url.Values{}
params.Set("q", q)
params.Set("limit", fmt.Sprint(limit))
params.Set("resolve", fmt.Sprint(resolve))

var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/search", params, &accounts, nil)
if err != nil {
return nil, err
}
return accounts, nil
}

// FollowRemoteUser sends follow-request.
func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, error) {
params := url.Values{}
Expand Down
38 changes: 38 additions & 0 deletions accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,44 @@ func TestAccountsSearch(t *testing.T) {
t.Fatalf("want %q but %q", "barfoo", res[1].Username)
}
}
func TestAccountsSearchResolve(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query()["q"][0] != "foo" {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
if r.FormValue("resolve") != "true" {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
fmt.Fprintln(w, `[{"username": "foobar"}, {"username": "barfoo"}]`)
}))
defer ts.Close()

client := NewClient(&Config{
Server: ts.URL,
ClientID: "foo",
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.AccountsSearchResolve(context.Background(), "zzz", 2, false)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
res, err := client.AccountsSearchResolve(context.Background(), "foo", 2, true)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
if len(res) != 2 {
t.Fatalf("result should be two: %d", len(res))
}
if res[0].Username != "foobar" {
t.Fatalf("want %q but %q", "foobar", res[0].Username)
}
if res[1].Username != "barfoo" {
t.Fatalf("want %q but %q", "barfoo", res[1].Username)
}
}

func TestFollowRemoteUser(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down
12 changes: 12 additions & 0 deletions filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ type Filter struct {
Irreversible bool `json:"irreversible"`
}

type FilterResult struct {
Filter struct {
ID string `json:"id"`
Title string `json:"title"`
Context []string `json:"context"`
ExpiresAt time.Time `json:"expires_at"`
FilterAction string `json:"filter_action"`
} `json:"filter"`
KeywordMatches []string `json:"keyword_matches"`
StatusMatches []string `json:"status_matches"`
}

// GetFilters returns all the filters on the current account.
func (c *Client) GetFilters(ctx context.Context) ([]*Filter, error) {
var filters []*Filter
Expand Down
4 changes: 2 additions & 2 deletions lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (c *Client) DeleteList(ctx context.Context, id ID) error {
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))
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)
Expand All @@ -100,7 +100,7 @@ func (c *Client) AddToList(ctx context.Context, list ID, accounts ...ID) error {
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))
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)
Expand Down
2 changes: 1 addition & 1 deletion lists_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func TestAddToList(t *testing.T) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
if r.PostFormValue("account_ids") != "1" {
if r.PostFormValue("account_ids[]") != "1" {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
Expand Down
7 changes: 0 additions & 7 deletions mastodon.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,6 @@ type Mention struct {
ID ID `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"`
Expand Down
1 change: 1 addition & 0 deletions mastodon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ func TestForTheCoverages(t *testing.T) {
(*UpdateEvent)(nil).event()
(*UpdateEditEvent)(nil).event()
(*NotificationEvent)(nil).event()
(*ConversationEvent)(nil).event()
(*DeleteEvent)(nil).event()
(*ErrorEvent)(nil).event()
_ = (&ErrorEvent{io.EOF}).Error()
Expand Down
13 changes: 12 additions & 1 deletion notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,19 @@ type PushAlerts struct {

// GetNotifications returns notifications.
func (c *Client) GetNotifications(ctx context.Context, pg *Pagination) ([]*Notification, error) {
return c.GetNotificationsExclude(ctx, nil, pg)
}

// GetNotificationsExclude returns notifications with excluded notifications
func (c *Client) GetNotificationsExclude(ctx context.Context, exclude *[]string, pg *Pagination) ([]*Notification, error) {
var notifications []*Notification
err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, &notifications, pg)
params := url.Values{}
if exclude != nil {
for _, ex := range *exclude {
params.Add("exclude_types[]", ex)
}
}
err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", params, &notifications, pg)
if err != nil {
return nil, err
}
Expand Down
16 changes: 15 additions & 1 deletion notification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ func TestGetNotifications(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v1/notifications":
fmt.Fprintln(w, `[{"id": 122, "action_taken": false}, {"id": 123, "action_taken": true}]`)
if r.URL.Query().Get("exclude_types[]") == "follow" {
fmt.Fprintln(w, `[{"id": 321, "action_taken": true}]`)
} else {
fmt.Fprintln(w, `[{"id": 122, "action_taken": false}, {"id": 123, "action_taken": true}]`)
}
return
case "/api/v1/notifications/123":
fmt.Fprintln(w, `{"id": 123, "action_taken": true}`)
Expand Down Expand Up @@ -50,6 +54,16 @@ func TestGetNotifications(t *testing.T) {
if ns[1].ID != "123" {
t.Fatalf("want %v but %v", "123", ns[1].ID)
}
nse, err := client.GetNotificationsExclude(context.Background(), &[]string{"follow"}, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
if len(nse) != 1 {
t.Fatalf("result should be one: %d", len(nse))
}
if nse[0].ID != "321" {
t.Fatalf("want %v but %v", "321", nse[0].ID)
}
n, err := client.GetNotification(context.Background(), "123")
if err != nil {
t.Fatalf("should not be fail: %v", err)
Expand Down
91 changes: 62 additions & 29 deletions status.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,36 @@ import (

// 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"`
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"`
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"`
ID ID `json:"id"`
URI string `json:"uri"`
URL string `json:"url"`
Account Account `json:"account"`
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"`
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"`
Filtered []FilterResult `json:"filtered"`
}

// StatusHistory is a struct to hold status history data.
Expand Down Expand Up @@ -102,6 +103,12 @@ type Media struct {
Focus string
}

type TagData struct {
Any []string
All []string
None []string
}

func (m *Media) bodyAndContentType() (io.Reader, string, error) {
var buf bytes.Buffer
mw := multipart.NewWriter(&buf)
Expand Down Expand Up @@ -349,6 +356,32 @@ func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal boo
return statuses, nil
}

// GetTimelineHashtag return statuses from tagged timeline.
func (c *Client) GetTimelineHashtagMultiple(ctx context.Context, tag string, isLocal bool, td *TagData, pg *Pagination) ([]*Status, error) {
params := url.Values{}
if isLocal {
params.Set("local", "t")
}
if td != nil {
for _, v := range td.Any {
params.Add("any[]", v)
}
for _, v := range td.All {
params.Add("all[]", v)
}
for _, v := range td.None {
params.Add("none[]", v)
}
}

var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", url.PathEscape(tag)), params, &statuses, pg)
if err != nil {
return nil, err
}
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
Expand Down
Loading