This commit is contained in:
gutmet 2022-11-20 21:31:59 +01:00
parent 7274b5e624
commit 1dfacac66a
11 changed files with 179 additions and 183 deletions

View File

@ -4,34 +4,6 @@ Fork of https://github.com/mattn/go-mastodon
## Usage ## Usage
### Application
```go
package main
import (
"context"
"fmt"
"log"
mastodon "git.gutmet.org/go-mastodon.git"
)
func main() {
app, err := mastodon.RegisterApp(context.Background(), &mastodon.AppConfig{
Server: "https://mstdn.jp",
ClientName: "client-name",
Scopes: "read write follow",
Website: "https://github.com/mattn/go-mastodon",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("client-id : %s\n", app.ClientID)
fmt.Printf("client-secret: %s\n", app.ClientSecret)
}
```
### Client ### Client
```go ```go
@ -46,16 +18,13 @@ import (
) )
func main() { func main() {
c := mastodon.NewClient(&mastodon.Config{ mastodon.Initialize(&mastodon.Config{
Server: "https://mstdn.jp", Server: "https://mstdn.jp",
ClientID: "client-id", ClientID: "client-id",
ClientSecret: "client-secret", ClientSecret: "client-secret",
AccessToken: "accessToken",
}) })
err := c.Authenticate(context.Background(), "your-email", "your-password") timeline, err := GetTimelineHome(nil)
if err != nil {
log.Fatal(err)
}
timeline, err := c.GetTimelineHome(context.Background(), nil)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -8,7 +8,6 @@ import (
"time" "time"
) )
// Account holds information for a mastodon account.
type Account struct { type Account struct {
ID ID `json:"id"` ID ID `json:"id"`
Username string `json:"username"` Username string `json:"username"`
@ -33,6 +32,14 @@ type Account struct {
Source *AccountSource `json:"source"` Source *AccountSource `json:"source"`
} }
func (account *Account) GetID() string {
if account != nil {
return string(account.ID)
} else {
return ""
}
}
// Field is a Mastodon account profile field. // Field is a Mastodon account profile field.
type Field struct { type Field struct {
Name string `json:"name"` Name string `json:"name"`
@ -40,7 +47,6 @@ type Field struct {
VerifiedAt time.Time `json:"verified_at"` VerifiedAt time.Time `json:"verified_at"`
} }
// AccountSource is a Mastodon account profile field.
type AccountSource struct { type AccountSource struct {
Privacy *string `json:"privacy"` Privacy *string `json:"privacy"`
Sensitive *bool `json:"sensitive"` Sensitive *bool `json:"sensitive"`
@ -126,38 +132,38 @@ func UpdateCurrentAccount(profile *Profile) (*Account, error) {
return &account, nil return &account, nil
} }
func GetAccountStatuses(id ID, pg *Pagination) ([]*Status, error) { func (a *Account) GetStatuses(pg *Pagination) ([]*Status, error) {
var statuses []*Status var statuses []*Status
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(string(id))), nil, &statuses, pg) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(a.GetID())), nil, &statuses, pg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return statuses, nil return statuses, nil
} }
func GetAccountPinnedStatuses(id ID) ([]*Status, error) { func (a *Account) GetPinnedStatuses() ([]*Status, error) {
var statuses []*Status var statuses []*Status
params := url.Values{} params := url.Values{}
params.Set("pinned", "true") params.Set("pinned", "true")
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(string(id))), params, &statuses, nil) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(a.GetID())), params, &statuses, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return statuses, nil return statuses, nil
} }
func GetAccountFollowers(id ID, pg *Pagination) ([]*Account, error) { func (a *Account) GetFollowers(pg *Pagination) ([]*Account, error) {
var accounts []*Account var accounts []*Account
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/followers", url.PathEscape(string(id))), nil, &accounts, pg) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/followers", url.PathEscape(a.GetID())), nil, &accounts, pg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return accounts, nil return accounts, nil
} }
func GetAccountFollowing(id ID, pg *Pagination) ([]*Account, error) { func (a *Account) GetFollowing(pg *Pagination) ([]*Account, error) {
var accounts []*Account var accounts []*Account
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/following", url.PathEscape(string(id))), nil, &accounts, pg) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/following", url.PathEscape(a.GetID())), nil, &accounts, pg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -173,7 +179,6 @@ func GetBlockList(pg *Pagination) ([]*Account, error) {
return accounts, nil return accounts, nil
} }
// Relationship holds information for relationship to the account.
type Relationship struct { type Relationship struct {
ID ID `json:"id"` ID ID `json:"id"`
Following bool `json:"following"` Following bool `json:"following"`
@ -187,61 +192,61 @@ type Relationship struct {
Endorsed bool `json:"endorsed"` Endorsed bool `json:"endorsed"`
} }
func FollowAccount(id ID) (*Relationship, error) { func Follow(account *Account) (*Relationship, error) {
var relationship Relationship var relationship Relationship
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/follow", url.PathEscape(string(id))), nil, &relationship, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/follow", url.PathEscape(account.GetID())), nil, &relationship, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &relationship, nil return &relationship, nil
} }
func UnfollowAccount(id ID) (*Relationship, error) { func Unfollow(account *Account) (*Relationship, error) {
var relationship Relationship var relationship Relationship
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unfollow", url.PathEscape(string(id))), nil, &relationship, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unfollow", url.PathEscape(account.GetID())), nil, &relationship, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &relationship, nil return &relationship, nil
} }
func BlockAccount(id ID) (*Relationship, error) { func Block(account *Account) (*Relationship, error) {
var relationship Relationship var relationship Relationship
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/block", url.PathEscape(string(id))), nil, &relationship, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/block", url.PathEscape(account.GetID())), nil, &relationship, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &relationship, nil return &relationship, nil
} }
func UnblockAccount(id ID) (*Relationship, error) { func Unblock(account *Account) (*Relationship, error) {
var relationship Relationship var relationship Relationship
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unblock", url.PathEscape(string(id))), nil, &relationship, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unblock", url.PathEscape(account.GetID())), nil, &relationship, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &relationship, nil return &relationship, nil
} }
func MuteAccount(id ID) (*Relationship, error) { func Mute(account *Account) (*Relationship, error) {
var relationship Relationship var relationship Relationship
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/mute", url.PathEscape(string(id))), nil, &relationship, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/mute", url.PathEscape(account.GetID())), nil, &relationship, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &relationship, nil return &relationship, nil
} }
func UnmuteAccount(id ID) (*Relationship, error) { func Unmute(account *Account) (*Relationship, error) {
var relationship Relationship var relationship Relationship
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unmute", url.PathEscape(string(id))), nil, &relationship, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unmute", url.PathEscape(account.GetID())), nil, &relationship, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &relationship, nil return &relationship, nil
} }
func GetAccountRelationships(ids []string) ([]*Relationship, error) { func GetRelationships(ids []string) ([]*Relationship, error) {
params := url.Values{} params := url.Values{}
for _, id := range ids { for _, id := range ids {
params.Add("id[]", id) params.Add("id[]", id)
@ -268,6 +273,7 @@ func SearchAccounts(q string, limit int64) ([]*Account, error) {
return accounts, nil return accounts, nil
} }
// uri: username@domain of the person you want to follow
func FollowRemoteUser(uri string) (*Account, error) { func FollowRemoteUser(uri string) (*Account, error) {
params := url.Values{} params := url.Values{}
params.Set("uri", uri) params.Set("uri", uri)
@ -289,12 +295,12 @@ func GetFollowRequests(pg *Pagination) ([]*Account, error) {
return accounts, nil return accounts, nil
} }
func AuthorizeFollowRequest(id ID) error { func AuthorizeFollowRequest(account *Account) error {
return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/authorize", url.PathEscape(string(id))), nil, nil, nil) return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/authorize", url.PathEscape(account.GetID())), nil, nil, nil)
} }
func RejectFollowRequest(id ID) error { func RejectFollowRequest(account *Account) error {
return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/reject", url.PathEscape(string(id))), nil, nil, nil) return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/reject", url.PathEscape(account.GetID())), nil, nil, nil)
} }
func GetMuteList(pg *Pagination) ([]*Account, error) { func GetMuteList(pg *Pagination) ([]*Account, error) {

View File

@ -9,7 +9,6 @@ import (
"strings" "strings"
) )
// AppConfig is a setting for registering applications.
type AppConfig struct { type AppConfig struct {
http.Client http.Client
Server string Server string
@ -27,7 +26,6 @@ type AppConfig struct {
Website string Website string
} }
// Application is a mastodon application.
type Application struct { type Application struct {
ID ID `json:"id"` ID ID `json:"id"`
RedirectURI string `json:"redirect_uri"` RedirectURI string `json:"redirect_uri"`
@ -38,7 +36,6 @@ type Application struct {
AuthURI string `json:"auth_uri,omitempty"` AuthURI string `json:"auth_uri,omitempty"`
} }
// RegisterApp returns the mastodon application.
func RegisterApp(appConfig *AppConfig) (*Application, error) { func RegisterApp(appConfig *AppConfig) (*Application, error) {
ctx := context.Background() ctx := context.Background()
params := url.Values{} params := url.Values{}

View File

@ -8,7 +8,6 @@ import (
"time" "time"
) )
// Filter is metadata for a filter of users.
type Filter struct { type Filter struct {
ID ID `json:"id"` ID ID `json:"id"`
Phrase string `json:"phrase"` Phrase string `json:"phrase"`
@ -18,6 +17,14 @@ type Filter struct {
Irreversible bool `json:"irreversible"` Irreversible bool `json:"irreversible"`
} }
func (f *Filter) GetID() string {
if f != nil {
return string(f.ID)
} else {
return ""
}
}
func GetFilters() ([]*Filter, error) { func GetFilters() ([]*Filter, error) {
var filters []*Filter var filters []*Filter
err := doAPI(http.MethodGet, "/api/v1/filters", nil, &filters, nil) err := doAPI(http.MethodGet, "/api/v1/filters", nil, &filters, nil)
@ -27,93 +34,92 @@ func GetFilters() ([]*Filter, error) {
return filters, nil return filters, nil
} }
// was ist id ? func GetFilter(filterId ID) (*Filter, error) {
func GetFilter(id ID) (*Filter, error) {
var filter Filter var filter Filter
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(string(id))), nil, &filter, nil) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(string(filterId))), nil, &filter, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &filter, nil return &filter, nil
} }
func CreateFilter(filter *Filter) (*Filter, error) { func (f *Filter) Create() (*Filter, error) {
if filter == nil { if f == nil {
return nil, errors.New("filter can't be nil") return nil, errors.New("filter can't be nil")
} }
if filter.Phrase == "" { if f.Phrase == "" {
return nil, errors.New("phrase can't be empty") return nil, errors.New("phrase can't be empty")
} }
if len(filter.Context) == 0 { if len(f.Context) == 0 {
return nil, errors.New("context can't be empty") return nil, errors.New("context can't be empty")
} }
params := url.Values{} params := url.Values{}
params.Set("phrase", filter.Phrase) params.Set("phrase", f.Phrase)
for _, c := range filter.Context { for _, c := range f.Context {
params.Add("context[]", c) params.Add("context[]", c)
} }
if filter.WholeWord { if f.WholeWord {
params.Add("whole_word", "true") params.Add("whole_word", "true")
} }
if filter.Irreversible { if f.Irreversible {
params.Add("irreversible", "true") params.Add("irreversible", "true")
} }
if !filter.ExpiresAt.IsZero() { if !f.ExpiresAt.IsZero() {
diff := time.Until(filter.ExpiresAt) diff := time.Until(f.ExpiresAt)
params.Add("expires_in", fmt.Sprintf("%.0f", diff.Seconds())) params.Add("expires_in", fmt.Sprintf("%.0f", diff.Seconds()))
} }
var f Filter var ret Filter
err := doAPI(http.MethodPost, "/api/v1/filters", params, &f, nil) err := doAPI(http.MethodPost, "/api/v1/filters", params, &ret, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &f, nil return &ret, nil
} }
func UpdateFilter(id ID, filter *Filter) (*Filter, error) { func (f *Filter) Update(newFilter *Filter) (*Filter, error) {
if filter == nil { if newFilter == nil {
return nil, errors.New("filter can't be nil") return nil, errors.New("filter can't be nil")
} }
if id == ID("") { if f.GetID() == "" {
return nil, errors.New("ID can't be empty") return nil, errors.New("ID can't be empty")
} }
if filter.Phrase == "" { if newFilter.Phrase == "" {
return nil, errors.New("phrase can't be empty") return nil, errors.New("phrase can't be empty")
} }
if len(filter.Context) == 0 { if len(newFilter.Context) == 0 {
return nil, errors.New("context can't be empty") return nil, errors.New("context can't be empty")
} }
params := url.Values{} params := url.Values{}
params.Set("phrase", filter.Phrase) params.Set("phrase", newFilter.Phrase)
for _, c := range filter.Context { for _, c := range newFilter.Context {
params.Add("context[]", c) params.Add("context[]", c)
} }
if filter.WholeWord { if newFilter.WholeWord {
params.Add("whole_word", "true") params.Add("whole_word", "true")
} else { } else {
params.Add("whole_word", "false") params.Add("whole_word", "false")
} }
if filter.Irreversible { if newFilter.Irreversible {
params.Add("irreversible", "true") params.Add("irreversible", "true")
} else { } else {
params.Add("irreversible", "false") params.Add("irreversible", "false")
} }
if !filter.ExpiresAt.IsZero() { if !newFilter.ExpiresAt.IsZero() {
diff := time.Until(filter.ExpiresAt) diff := time.Until(newFilter.ExpiresAt)
params.Add("expires_in", fmt.Sprintf("%.0f", diff.Seconds())) params.Add("expires_in", fmt.Sprintf("%.0f", diff.Seconds()))
} else { } else {
params.Add("expires_in", "") params.Add("expires_in", "")
} }
var f Filter var ret Filter
err := doAPI(http.MethodPut, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(string(id))), params, &f, nil) err := doAPI(http.MethodPut, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(f.GetID())), params, &ret, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &f, nil return &ret, nil
} }
func DeleteFilter(id ID) error { func (f *Filter) Delete() error {
return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(string(id))), nil, nil, nil) return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/filters/%s", url.PathEscape(f.GetID())), nil, nil, nil)
} }

View File

@ -6,12 +6,19 @@ import (
"net/url" "net/url"
) )
// List is metadata for a list of users.
type List struct { type List struct {
ID ID `json:"id"` ID ID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
} }
func (l *List) GetID() string {
if l != nil {
return string(l.ID)
} else {
return ""
}
}
func GetLists() ([]*List, error) { func GetLists() ([]*List, error) {
var lists []*List var lists []*List
err := doAPI(http.MethodGet, "/api/v1/lists", nil, &lists, nil) err := doAPI(http.MethodGet, "/api/v1/lists", nil, &lists, nil)
@ -21,10 +28,9 @@ func GetLists() ([]*List, error) {
return lists, nil return lists, nil
} }
// GetAccountLists returns the lists containing a given account. func (a *Account) GetContainingLists() ([]*List, error) {
func GetListWithAccount(id ID) ([]*List, error) {
var lists []*List var lists []*List
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/lists", url.PathEscape(string(id))), nil, &lists, nil) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/lists", url.PathEscape(a.GetID())), nil, &lists, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -61,34 +67,32 @@ func CreateList(title string) (*List, error) {
return &list, nil return &list, nil
} }
func RenameList(list *List, newTitle string) error { func (l *List) Rename(newTitle string) error {
params := url.Values{} params := url.Values{}
params.Set("title", newTitle) params.Set("title", newTitle)
return doAPI(http.MethodPut, fmt.Sprintf("/api/v1/lists/%s", url.PathEscape(string(list.ID))), params, nil, nil) return doAPI(http.MethodPut, fmt.Sprintf("/api/v1/lists/%s", url.PathEscape(l.GetID())), params, nil, nil)
} }
func DeleteList(id ID) error { func (l *List) Delete() error {
return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/lists/%s", url.PathEscape(string(id))), nil, nil, nil) return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/lists/%s", url.PathEscape(l.GetID())), nil, nil, nil)
} }
// AddToList adds accounts to a list.
//
// Only accounts already followed by the user can be added to a list. // Only accounts already followed by the user can be added to a list.
func AddToList(list ID, accounts ...ID) error { func (l *List) Add(accounts ...*Account) error {
params := url.Values{} params := url.Values{}
for _, acct := range accounts { for _, a := range accounts {
params.Add("account_ids", string(acct)) params.Add("account_ids", string(a.GetID()))
} }
return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(string(list))), params, nil, nil) return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(l.GetID())), params, nil, nil)
} }
func RemoveFromList(list ID, accounts ...ID) error { func (l *List) Remove(accounts ...*Account) error {
params := url.Values{} params := url.Values{}
for _, acct := range accounts { for _, a := range accounts {
params.Add("account_ids", string(acct)) params.Add("account_ids", a.GetID())
} }
return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(string(list))), params, nil, nil) return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/lists/%s/accounts", url.PathEscape(l.GetID())), params, nil, nil)
} }

View File

@ -14,7 +14,6 @@ import (
"time" "time"
) )
// Config is a setting for access mastodon APIs.
type Config struct { type Config struct {
Server string Server string
ClientID string ClientID string
@ -22,7 +21,6 @@ type Config struct {
AccessToken string AccessToken string
} }
// Client is a API client for mastodon.
type Client struct { type Client struct {
http.Client http.Client
Config *Config Config *Config
@ -141,7 +139,6 @@ func doAPI(method string, uri string, params interface{}, res interface{}, pg *P
return json.NewDecoder(resp.Body).Decode(&res) return json.NewDecoder(resp.Body).Decode(&res)
} }
// NewClient returns a new mastodon API client.
func newClient(config *Config) *Client { func newClient(config *Config) *Client {
return &Client{ return &Client{
Client: *http.DefaultClient, Client: *http.DefaultClient,
@ -227,7 +224,6 @@ func (c *Client) authenticate(ctx context.Context, params url.Values) error {
return nil return nil
} }
// Convenience constants for Toot.Visibility
const ( const (
VisibilityPublic = "public" VisibilityPublic = "public"
VisibilityUnlisted = "unlisted" VisibilityUnlisted = "unlisted"
@ -235,7 +231,6 @@ const (
VisibilityDirectMessage = "direct" VisibilityDirectMessage = "direct"
) )
// Toot is a struct to post status.
type Toot struct { type Toot struct {
Status string `json:"status"` Status string `json:"status"`
InReplyToID ID `json:"in_reply_to_id"` InReplyToID ID `json:"in_reply_to_id"`
@ -248,7 +243,6 @@ type Toot struct {
Poll *TootPoll `json:"poll"` Poll *TootPoll `json:"poll"`
} }
// TootPoll holds information for creating a poll in Toot.
type TootPoll struct { type TootPoll struct {
Options []string `json:"options"` Options []string `json:"options"`
ExpiresInSeconds int64 `json:"expires_in"` ExpiresInSeconds int64 `json:"expires_in"`
@ -256,7 +250,6 @@ type TootPoll struct {
HideTotals bool `json:"hide_totals"` HideTotals bool `json:"hide_totals"`
} }
// Mention hold information for mention.
type Mention struct { type Mention struct {
URL string `json:"url"` URL string `json:"url"`
Username string `json:"username"` Username string `json:"username"`
@ -264,21 +257,18 @@ type Mention struct {
ID ID `json:"id"` ID ID `json:"id"`
} }
// Tag hold information for tag.
type Tag struct { type Tag struct {
Name string `json:"name"` Name string `json:"name"`
URL string `json:"url"` URL string `json:"url"`
History []History `json:"history"` History []History `json:"history"`
} }
// History hold information for history.
type History struct { type History struct {
Day string `json:"day"` Day string `json:"day"`
Uses string `json:"uses"` Uses string `json:"uses"`
Accounts string `json:"accounts"` Accounts string `json:"accounts"`
} }
// Attachment hold information for attachment.
type Attachment struct { type Attachment struct {
ID ID `json:"id"` ID ID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
@ -290,13 +280,11 @@ type Attachment struct {
Meta AttachmentMeta `json:"meta"` Meta AttachmentMeta `json:"meta"`
} }
// AttachmentMeta holds information for attachment metadata.
type AttachmentMeta struct { type AttachmentMeta struct {
Original AttachmentSize `json:"original"` Original AttachmentSize `json:"original"`
Small AttachmentSize `json:"small"` Small AttachmentSize `json:"small"`
} }
// AttachmentSize holds information for attatchment size.
type AttachmentSize struct { type AttachmentSize struct {
Width int64 `json:"width"` Width int64 `json:"width"`
Height int64 `json:"height"` Height int64 `json:"height"`
@ -304,7 +292,6 @@ type AttachmentSize struct {
Aspect float64 `json:"aspect"` Aspect float64 `json:"aspect"`
} }
// Emoji hold information for CustomEmoji.
type Emoji struct { type Emoji struct {
ShortCode string `json:"shortcode"` ShortCode string `json:"shortcode"`
StaticURL string `json:"static_url"` StaticURL string `json:"static_url"`
@ -312,7 +299,6 @@ type Emoji struct {
VisibleInPicker bool `json:"visible_in_picker"` VisibleInPicker bool `json:"visible_in_picker"`
} }
// Results hold information for search result.
type Results struct { type Results struct {
Accounts []*Account `json:"accounts"` Accounts []*Account `json:"accounts"`
Statuses []*Status `json:"statuses"` Statuses []*Status `json:"statuses"`

View File

@ -11,7 +11,6 @@ import (
"time" "time"
) )
// Notification holds information for a mastodon notification.
type Notification struct { type Notification struct {
ID ID `json:"id"` ID ID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
@ -20,6 +19,14 @@ type Notification struct {
Status *Status `json:"status"` Status *Status `json:"status"`
} }
func (n *Notification) GetID() string {
if n != nil {
return string(n.ID)
} else {
return ""
}
}
type PushSubscription struct { type PushSubscription struct {
ID ID `json:"id"` ID ID `json:"id"`
Endpoint string `json:"endpoint"` Endpoint string `json:"endpoint"`
@ -52,8 +59,8 @@ func GetNotification(id ID) (*Notification, error) {
return &notification, nil return &notification, nil
} }
func DismissNotification(id ID) error { func (n *Notification) Dismiss() error {
return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/notifications/%v/dismiss", id), nil, nil, nil) return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/notifications/%v/dismiss", n.GetID()), nil, nil, nil)
} }
func ClearNotifications() error { func ClearNotifications() error {

View File

@ -20,6 +20,14 @@ type Poll struct {
Emojis []Emoji `json:"emojis"` Emojis []Emoji `json:"emojis"`
} }
func (p *Poll) GetID() string {
if p != nil {
return string(p.ID)
} else {
return ""
}
}
type PollOption struct { type PollOption struct {
Title string `json:"title"` Title string `json:"title"`
VotesCount int64 `json:"votes_count"` VotesCount int64 `json:"votes_count"`
@ -34,14 +42,14 @@ func GetPoll(id ID) (*Poll, error) {
return &poll, nil return &poll, nil
} }
func PollVote(id ID, choices ...int) (*Poll, error) { func (p *Poll) Vote(choices ...int) (*Poll, error) {
params := url.Values{} params := url.Values{}
for _, c := range choices { for _, c := range choices {
params.Add("choices[]", fmt.Sprintf("%d", c)) params.Add("choices[]", fmt.Sprintf("%d", c))
} }
var poll Poll var poll Poll
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/polls/%s/votes", url.PathEscape(string(id))), params, &poll, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/polls/%s/votes", url.PathEscape(p.GetID())), params, &poll, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -5,7 +5,6 @@ import (
"net/url" "net/url"
) )
// Report holds information for a mastodon report.
type Report struct { type Report struct {
ID int64 `json:"id"` ID int64 `json:"id"`
ActionTaken bool `json:"action_taken"` ActionTaken bool `json:"action_taken"`

View File

@ -43,6 +43,14 @@ type Status struct {
Pinned interface{} `json:"pinned"` Pinned interface{} `json:"pinned"`
} }
func (s *Status) GetID() string {
if s != nil {
return string(s.ID)
} else {
return ""
}
}
type Context struct { type Context struct {
Ancestors []*Status `json:"ancestors"` Ancestors []*Status `json:"ancestors"`
Descendants []*Status `json:"descendants"` Descendants []*Status `json:"descendants"`
@ -70,6 +78,14 @@ type Conversation struct {
LastStatus *Status `json:"last_status"` LastStatus *Status `json:"last_status"`
} }
func (c *Conversation) GetID() string {
if c != nil {
return string(c.ID)
} else {
return ""
}
}
type Media struct { type Media struct {
File io.Reader File io.Reader
Thumbnail io.Reader Thumbnail io.Reader
@ -161,97 +177,97 @@ func GetStatus(id ID) (*Status, error) {
return &status, nil return &status, nil
} }
func GetStatusContext(id ID) (*Context, error) { func (s *Status) GetContext() (*Context, error) {
var context Context var context Context
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/context", id), nil, &context, nil) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/context", s.GetID()), nil, &context, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &context, nil return &context, nil
} }
func GetStatusCard(id ID) (*Card, error) { func (s *Status) GetCard() (*Card, error) {
var card Card var card Card
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/card", id), nil, &card, nil) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/card", s.GetID()), nil, &card, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &card, nil return &card, nil
} }
func GetRebloggedBy(id ID, pg *Pagination) ([]*Account, error) { func (s *Status) GetRebloggedBy(pg *Pagination) ([]*Account, error) {
var accounts []*Account var accounts []*Account
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/reblogged_by", id), nil, &accounts, pg) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/reblogged_by", s.GetID()), nil, &accounts, pg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return accounts, nil return accounts, nil
} }
func GetFavouritedBy(id ID, pg *Pagination) ([]*Account, error) { func (s *Status) GetFavouritedBy(pg *Pagination) ([]*Account, error) {
var accounts []*Account var accounts []*Account
err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/favourited_by", id), nil, &accounts, pg) err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/favourited_by", s.GetID()), nil, &accounts, pg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return accounts, nil return accounts, nil
} }
func Reblog(id ID) (*Status, error) { func (s *Status) DoReblog() (*Status, error) {
var status Status var status Status
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/reblog", id), nil, &status, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/reblog", s.GetID()), nil, &status, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &status, nil return &status, nil
} }
func Unreblog(id ID) (*Status, error) { func (s *Status) Unreblog() (*Status, error) {
var status Status var status Status
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unreblog", id), nil, &status, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unreblog", s.GetID()), nil, &status, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &status, nil return &status, nil
} }
func Favourite(id ID) (*Status, error) { func (s *Status) Favourite() (*Status, error) {
var status Status var status Status
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/favourite", id), nil, &status, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/favourite", s.GetID()), nil, &status, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &status, nil return &status, nil
} }
func Unfavourite(id ID) (*Status, error) { func (s *Status) Unfavourite() (*Status, error) {
var status Status var status Status
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unfavourite", id), nil, &status, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unfavourite", s.GetID()), nil, &status, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &status, nil return &status, nil
} }
func Bookmark(id ID) (*Status, error) { func (s *Status) Bookmark() (*Status, error) {
var status Status var status Status
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/bookmark", id), nil, &status, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/bookmark", s.GetID()), nil, &status, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &status, nil return &status, nil
} }
func (c *Client) Unbookmark(id ID) (*Status, error) { func (s *Status) Unbookmark() (*Status, error) {
var status Status var status Status
err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unbookmark", id), nil, &status, nil) err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unbookmark", s.GetID()), nil, &status, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &status, nil return &status, nil
} }
func (c *Client) GetHomeTimeline(pg *Pagination) ([]*Status, error) { func GetHomeTimeline(pg *Pagination) ([]*Status, error) {
var statuses []*Status var statuses []*Status
err := doAPI(http.MethodGet, "/api/v1/timelines/home", nil, &statuses, pg) err := doAPI(http.MethodGet, "/api/v1/timelines/home", nil, &statuses, pg)
if err != nil { if err != nil {
@ -359,8 +375,8 @@ func PostStatus(toot *Toot) (*Status, error) {
return &status, nil return &status, nil
} }
func DeleteStatus(id ID) error { func (s *Status) Delete() error {
return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%s", id), nil, nil, nil) return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%s", s.GetID()), nil, nil, nil)
} }
func Search(q string, resolve bool) (*Results, error) { func Search(q string, resolve bool) (*Results, error) {
@ -431,10 +447,10 @@ func GetConversations(pg *Pagination) ([]*Conversation, error) {
return conversations, nil return conversations, nil
} }
func DeleteConversation(id ID) error { func (c *Conversation) Delete() error {
return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/conversations/%s", id), nil, nil, nil) return doAPI(http.MethodDelete, fmt.Sprintf("/api/v1/conversations/%s", c.GetID()), nil, nil, nil)
} }
func MarkConversationAsRead(id ID) error { func (c *Conversation) MarkAsRead() error {
return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/conversations/%s/read", id), nil, nil, nil) return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/conversations/%s/read", c.GetID()), nil, nil, nil)
} }

View File

@ -13,32 +13,27 @@ import (
"strings" "strings"
) )
// UpdateEvent is a struct for passing status event to app.
type UpdateEvent struct { type UpdateEvent struct {
Status *Status `json:"status"` Status *Status `json:"status"`
} }
func (e *UpdateEvent) event() {} func (e *UpdateEvent) event() {}
// NotificationEvent is a struct for passing notification event to app.
type NotificationEvent struct { type NotificationEvent struct {
Notification *Notification `json:"notification"` Notification *Notification `json:"notification"`
} }
func (e *NotificationEvent) event() {} func (e *NotificationEvent) event() {}
// DeleteEvent is a struct for passing deletion event to app.
type DeleteEvent struct{ ID ID } type DeleteEvent struct{ ID ID }
func (e *DeleteEvent) event() {} func (e *DeleteEvent) event() {}
// ErrorEvent is a struct for passing errors to app.
type ErrorEvent struct{ err error } type ErrorEvent struct{ err error }
func (e *ErrorEvent) event() {} func (e *ErrorEvent) event() {}
func (e *ErrorEvent) Error() string { return e.err.Error() } func (e *ErrorEvent) Error() string { return e.err.Error() }
// Event is an interface passing events to app.
type Event interface { type Event interface {
event() event()
} }
@ -97,7 +92,10 @@ func handleReader(q chan Event, r io.Reader) error {
} }
} }
func (c *Client) streaming(ctx context.Context, p string, params url.Values) (chan Event, error) { func streaming(p string, params url.Values) (chan Event, error) {
checkInit()
c := client
ctx := context.Background()
u, err := url.Parse(c.Config.Server) u, err := url.Parse(c.Config.Server)
if err != nil { if err != nil {
return nil, err return nil, err
@ -125,14 +123,14 @@ func (c *Client) streaming(ctx context.Context, p string, params url.Values) (ch
default: default:
} }
c.doStreaming(req, q) doStreaming(req, q)
} }
}() }()
return q, nil return q, nil
} }
func (c *Client) doStreaming(req *http.Request, q chan Event) { func doStreaming(req *http.Request, q chan Event) {
resp, err := c.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
q <- &ErrorEvent{err} q <- &ErrorEvent{err}
return return
@ -151,22 +149,22 @@ func (c *Client) doStreaming(req *http.Request, q chan Event) {
} }
// StreamingUser returns a channel to read events on home. // StreamingUser returns a channel to read events on home.
func (c *Client) StreamingUser(ctx context.Context) (chan Event, error) { func StreamingUser() (chan Event, error) {
return c.streaming(ctx, "user", nil) return streaming("user", nil)
} }
// StreamingPublic returns a channel to read events on public. // StreamingPublic returns a channel to read events on public.
func (c *Client) StreamingPublic(ctx context.Context, isLocal bool) (chan Event, error) { func StreamingPublic(isLocal bool) (chan Event, error) {
p := "public" p := "public"
if isLocal { if isLocal {
p = path.Join(p, "local") p = path.Join(p, "local")
} }
return c.streaming(ctx, p, nil) return streaming(p, nil)
} }
// StreamingHashtag returns a channel to read events on tagged timeline. // StreamingHashtag returns a channel to read events on tagged timeline.
func (c *Client) StreamingHashtag(ctx context.Context, tag string, isLocal bool) (chan Event, error) { func StreamingHashtag(tag string, isLocal bool) (chan Event, error) {
params := url.Values{} params := url.Values{}
params.Set("tag", tag) params.Set("tag", tag)
@ -175,18 +173,18 @@ func (c *Client) StreamingHashtag(ctx context.Context, tag string, isLocal bool)
p = path.Join(p, "local") p = path.Join(p, "local")
} }
return c.streaming(ctx, p, params) return streaming(p, params)
} }
// StreamingList returns a channel to read events on a list. // StreamingList returns a channel to read events on a list.
func (c *Client) StreamingList(ctx context.Context, id ID) (chan Event, error) { func (l *List) Streaming() (chan Event, error) {
params := url.Values{} params := url.Values{}
params.Set("list", string(id)) params.Set("list", l.GetID())
return c.streaming(ctx, "list", params) return streaming("list", params)
} }
// StreamingDirect returns a channel to read events on a direct messages. // StreamingDirect returns a channel to read events on a direct messages.
func (c *Client) StreamingDirect(ctx context.Context) (chan Event, error) { func StreamingDirect() (chan Event, error) {
return c.streaming(ctx, "direct", nil) return streaming("direct", nil)
} }