package mastodon import ( "fmt" "net/http" "net/url" "strconv" "time" ) 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"` } func (a *Account) GetID() string { if a != nil { return string(a.ID) } else { return "" } } // Field is a Mastodon account profile field. type Field struct { Name string `json:"name"` Value string `json:"value"` VerifiedAt time.Time `json:"verified_at"` } type AccountSource struct { Privacy *string `json:"privacy"` Sensitive *bool `json:"sensitive"` Language *string `json:"language"` Note *string `json:"note"` Fields *[]Field `json:"fields"` } func LookupAccount(username string) (*Account, error) { params := url.Values{} params.Set("acct", username) var account Account err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/lookup"), params, &account, nil) if err != nil { return nil, err } return &account, nil } func GetAccount(id ID) (*Account, error) { var account Account err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s", url.PathEscape(string(id))), nil, &account, nil) if err != nil { return nil, err } return &account, nil } func GetCurrentAccount() (*Account, error) { var account Account err := doAPI(http.MethodGet, "/api/v1/accounts/verify_credentials", nil, &account, nil) if err != nil { return nil, err } return &account, nil } type Profile struct { // If it is nil it will not be updated. // 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 Header string } func UpdateCurrentAccount(profile *Profile) (*Account, error) { params := url.Values{} if profile.DisplayName != nil { params.Set("display_name", *profile.DisplayName) } 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) } if profile.Header != "" { params.Set("header", profile.Header) } var account Account err := doAPI(http.MethodPatch, "/api/v1/accounts/update_credentials", params, &account, nil) if err != nil { return nil, err } return &account, nil } func (a *Account) GetStatuses(excludeBoosts bool, pg *Pagination) ([]*Status, error) { var statuses []*Status params := url.Values{} params.Set("limit", "100") if excludeBoosts { params.Set("exclude_reblogs", "true") } err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(a.GetID())), params, &statuses, pg) if err != nil { return nil, err } return statuses, nil } func (a *Account) GetPinnedStatuses() ([]*Status, error) { var statuses []*Status params := url.Values{} params.Set("pinned", "true") err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(a.GetID())), params, &statuses, nil) if err != nil { return nil, err } return statuses, nil } func (a *Account) GetFollowers(pg *Pagination) ([]*Account, error) { var accounts []*Account err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/followers", url.PathEscape(a.GetID())), nil, &accounts, pg) if err != nil { return nil, err } return accounts, nil } func (a *Account) GetFollowing(pg *Pagination) ([]*Account, error) { var accounts []*Account err := doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/following", url.PathEscape(a.GetID())), nil, &accounts, pg) if err != nil { return nil, err } return accounts, nil } func GetBlockList(pg *Pagination) ([]*Account, error) { var accounts []*Account err := doAPI(http.MethodGet, "/api/v1/blocks", nil, &accounts, pg) if err != nil { return nil, err } return accounts, nil } 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"` } func Follow(account *Account) (*Relationship, error) { var relationship Relationship err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/follow", url.PathEscape(account.GetID())), nil, &relationship, nil) if err != nil { return nil, err } return &relationship, nil } func Unfollow(account *Account) (*Relationship, error) { var relationship Relationship err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unfollow", url.PathEscape(account.GetID())), nil, &relationship, nil) if err != nil { return nil, err } return &relationship, nil } func Block(account *Account) (*Relationship, error) { var relationship Relationship err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/block", url.PathEscape(account.GetID())), nil, &relationship, nil) if err != nil { return nil, err } return &relationship, nil } func Unblock(account *Account) (*Relationship, error) { var relationship Relationship err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unblock", url.PathEscape(account.GetID())), nil, &relationship, nil) if err != nil { return nil, err } return &relationship, nil } func Mute(account *Account) (*Relationship, error) { var relationship Relationship err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/mute", url.PathEscape(account.GetID())), nil, &relationship, nil) if err != nil { return nil, err } return &relationship, nil } func Unmute(account *Account) (*Relationship, error) { var relationship Relationship err := doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/unmute", url.PathEscape(account.GetID())), nil, &relationship, nil) if err != nil { return nil, err } return &relationship, nil } func GetRelationships(accounts []*Account) ([]*Relationship, error) { params := url.Values{} for _, a := range accounts { params.Add("id[]", a.GetID()) } var relationships []*Relationship err := doAPI(http.MethodGet, "/api/v1/accounts/relationships", params, &relationships, nil) if err != nil { return nil, err } return relationships, nil } func SearchAccounts(q string, limit int64) ([]*Account, error) { params := url.Values{} params.Set("q", q) params.Set("limit", fmt.Sprint(limit)) var accounts []*Account err := doAPI(http.MethodGet, "/api/v1/accounts/search", params, &accounts, nil) if err != nil { return nil, err } return accounts, nil } // uri: username@domain of the person you want to follow func FollowRemoteUser(uri string) (*Account, error) { params := url.Values{} params.Set("uri", uri) var account Account err := doAPI(http.MethodPost, "/api/v1/follows", params, &account, nil) if err != nil { return nil, err } return &account, nil } func GetFollowRequests(pg *Pagination) ([]*Account, error) { var accounts []*Account err := doAPI(http.MethodGet, "/api/v1/follow_requests", nil, &accounts, pg) if err != nil { return nil, err } return accounts, nil } func AuthorizeFollowRequest(account *Account) error { return doAPI(http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/authorize", url.PathEscape(account.GetID())), nil, nil, nil) } func RejectFollowRequest(account *Account) error { 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) { var accounts []*Account err := doAPI(http.MethodGet, "/api/v1/mutes", nil, &accounts, pg) if err != nil { return nil, err } return accounts, nil }