123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- package mastodon
- import (
- "bytes"
- "context"
- "fmt"
- "encoding/json"
- "io"
- "mime/multipart"
- "net/http"
- "net/url"
- "path/filepath"
- "strconv"
- "time"
- "strings"
- "path"
- )
- type AccountPleroma struct {
- Relationship Relationship `json:"relationship"`
- IsAdmin bool `json:"is_admin"`
- IsModerator bool `json:"is_moderator"`
- IsConfirmed bool `json:"is_confirmed"`
- AcceptsChatMessages bool `json:"accepts_chat_messages"`
- HideFavourites *bool `json:"hide_favorites"`
- }
- // Account hold information for mastodon account.
- type Account struct {
- ID string `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"`
- Source *AccountSource `json:"source"`
- Pleroma *AccountPleroma `json:"pleroma"`
- MastodonAccount bool
- }
- // 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"`
- }
- type RegisterCredintals struct {
- http.Client
- Server string
- App *Application
- Reason string
- Username string
- Email string
- Password string
- Agreement bool
- Locale string
- }
- // Use for register account on GoToSocial
- type Registred struct {
- AccessToken string `json:"access_token"`
- CreatedAt int64 `json:"created_at"`
- Scope string `json:"scope"`
- TokenType string `json:"token_type"`
- }
- // RegisterAccount create new account
- func RegisterAccount(ctx context.Context, instance string, reason string, username string, email string, password string, agreement bool, locale string, registerCrendintals RegisterCredintals, bearer string) (*string, error) {
- var registred Registred
- params := url.Values{}
- params.Set("reason", reason)
- params.Set("username", username)
- params.Set("email", email)
- params.Set("password", password)
- params.Set("agreement", strconv.FormatBool(agreement))
- params.Set("locale", locale)
- u, err := url.Parse("https://" + instance)
- if err != nil {
- return nil, err
- }
- u.Path = path.Join(u.Path, "/api/v1/accounts")
- req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(params.Encode()))
- if err != nil {
- return nil, err
- }
- req = req.WithContext(ctx)
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- req.Header.Set("Authorization", "Bearer "+bearer)
- resp, err := registerCrendintals.Do(req)
- fmt.Println(req)
- fmt.Println(resp)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return nil, parseAPIError("bad request", resp)
- }
-
- err = json.NewDecoder(resp.Body).Decode(®istred)
- fmt.Println(resp.Body)
- if err != nil {
- return nil, err
- }
- return ®istred.AccessToken, nil
- }
- // GetAccount return Account.
- func (c *Client) GetAccount(ctx context.Context, id string) (*Account, error) {
- var account Account
- params := url.Values{}
- params.Set("with_relationships", strconv.FormatBool(true))
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s", url.PathEscape(string(id))), params, &account, nil)
- if err != nil {
- return nil, err
- }
- if account.Pleroma == nil || len(account.Pleroma.Relationship.ID) < 1 {
- rs, err := c.GetAccountRelationships(ctx, []string{id})
- if err != nil {
- return nil, err
- }
- if len(rs) > 0 {
- if account.Pleroma != nil {
- account.Pleroma = &AccountPleroma{*rs[0],
- account.Pleroma.IsAdmin,
- account.Pleroma.IsModerator,
- account.Pleroma.IsConfirmed,
- account.Pleroma.AcceptsChatMessages,
- account.Pleroma.HideFavourites,
- }
- } else {
- account.MastodonAccount = true
- account.Pleroma = &AccountPleroma{*rs[0], false, false, false, false, nil}
- }
- }
- }
- return &account, nil
- }
- // 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)
- if err != nil {
- return nil, err
- }
- return &account, nil
- }
- // Profile is a struct for updating profiles.
- 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 *multipart.FileHeader
- Header *multipart.FileHeader
- //Other settings
- Bot *bool
- Pleroma *ProfilePleroma
- }
- type ProfilePleroma struct {
- AcceptsChatMessages *bool
- ActorType *string
- AllowFollowingMove *bool
- Discoverable *bool
- HideFavourites *bool
- HideFollowers *bool
- HideFollows *bool
- HideFollowersCount *bool
- HideFollowsCount *bool
- Avatar *multipart.FileHeader
- Header *multipart.FileHeader
- }
- // AccountUpdate updates the information of the current user.
- func (c *Client) AccountUpdate(ctx context.Context, profile *Profile) (*Account, error) {
- var buf bytes.Buffer
- mw := multipart.NewWriter(&buf)
- if profile.DisplayName != nil {
- err := mw.WriteField("display_name", *profile.DisplayName)
- if err != nil {
- return nil, err
- }
- }
- if profile.Note != nil {
- err := mw.WriteField("note", *profile.Note)
- if err != nil {
- return nil, err
- }
- }
- if profile.Locked != nil {
- err := mw.WriteField("locked", strconv.FormatBool(*profile.Locked))
- if err != nil {
- return nil, err
- }
- }
- if profile.Fields != nil {
- for idx, field := range *profile.Fields {
- err := mw.WriteField(fmt.Sprintf("fields_attributes[%d][name]", idx), field.Name)
- if err != nil {
- return nil, err
- }
- err = mw.WriteField(fmt.Sprintf("fields_attributes[%d][value]", idx), field.Value)
- if err != nil {
- return nil, err
- }
- }
- }
- if profile.Avatar != nil {
- f, err := profile.Avatar.Open()
- if err != nil {
- return nil, err
- }
- fname := filepath.Base(profile.Avatar.Filename)
- part, err := mw.CreateFormFile("avatar", fname)
- if err != nil {
- return nil, err
- }
- _, err = io.Copy(part, f)
- if err != nil {
- return nil, err
- }
- }
- if profile.Header != nil {
- f, err := profile.Header.Open()
- if err != nil {
- return nil, err
- }
- fname := filepath.Base(profile.Header.Filename)
- part, err := mw.CreateFormFile("header", fname)
- if err != nil {
- return nil, err
- }
- _, err = io.Copy(part, f)
- if err != nil {
- return nil, err
- }
- }
- err := mw.Close()
- if err != nil {
- return nil, err
- }
- params := &multipartRequest{Data: &buf, ContentType: mw.FormDataContentType()}
- var account Account
- err = c.doAPI(ctx, http.MethodPatch, "/api/v1/accounts/update_credentials", params, &account, nil)
- if err != nil {
- return nil, err
- }
- return &account, nil
- }
- func (c *Client) accountDeleteField(ctx context.Context, field string) (*Account, error) {
- var buf bytes.Buffer
- mw := multipart.NewWriter(&buf)
- _, err := mw.CreateFormField(field)
- if err != nil {
- return nil, err
- }
- err = mw.Close()
- if err != nil {
- return nil, err
- }
- params := &multipartRequest{Data: &buf, ContentType: mw.FormDataContentType()}
- var account Account
- err = c.doAPI(ctx, http.MethodPatch, "/api/v1/accounts/update_credentials", params, &account, nil)
- if err != nil {
- return nil, err
- }
- return &account, nil
- }
- func (c *Client) AccountDeleteAvatar(ctx context.Context) (*Account, error) {
- return c.accountDeleteField(ctx, "avatar")
- }
- func (c *Client) AccountDeleteHeader(ctx context.Context) (*Account, error) {
- return c.accountDeleteField(ctx, "header")
- }
- // GetAccountStatuses return statuses by specified accuont.
- func (c *Client) GetAccountStatuses(ctx context.Context, id string, onlyMedia bool, onlyPinned bool, pg *Pagination) ([]*Status, error) {
- var statuses []*Status
- params := url.Values{}
- params.Set("only_media", strconv.FormatBool(onlyMedia))
- params.Set("pinned", strconv.FormatBool(onlyPinned))
- err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%s/statuses", url.PathEscape(string(id))), params, &statuses, pg)
- if err != nil {
- return nil, err
- }
- return statuses, nil
- }
- // GetAccountFollowers return followers list.
- func (c *Client) GetAccountFollowers(ctx context.Context, id string, 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)
- if err != nil {
- return nil, err
- }
- return accounts, nil
- }
- // GetAccountFollowing return following list.
- func (c *Client) GetAccountFollowing(ctx context.Context, id string, 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)
- if err != nil {
- return nil, err
- }
- return accounts, nil
- }
- // 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)
- if err != nil {
- return nil, err
- }
- return accounts, nil
- }
- // Relationship hold information for relation-ship to the account.
- type Relationship struct {
- ID string `json:"id"`
- Following bool `json:"following"`
- FollowedBy bool `json:"followed_by"`
- Blocking bool `json:"blocking"`
- BlockedBy bool `json:"blocked_by"`
- Muting bool `json:"muting"`
- MutingNotifications bool `json:"muting_notifications"`
- Subscribing bool `json:"subscribing"`
- Requested bool `json:"requested"`
- DomainBlocking bool `json:"domain_blocking"`
- ShowingReblogs bool `json:"showing_reblogs"`
- Endorsed bool `json:"endorsed"`
- }
- // AccountFollow follow the account.
- func (c *Client) AccountFollow(ctx context.Context, id string, reblogs *bool) (*Relationship, error) {
- var relationship Relationship
- params := url.Values{}
- if reblogs != nil {
- params.Set("reblogs", strconv.FormatBool(*reblogs))
- }
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/follow", url.PathEscape(id)), params, &relationship, nil)
- if err != nil {
- return nil, err
- }
- return &relationship, nil
- }
- // AccountUnfollow unfollow the account.
- func (c *Client) AccountUnfollow(ctx context.Context, id string) (*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)
- if err != nil {
- return nil, err
- }
- return &relationship, nil
- }
- // AccountBlock block the account.
- func (c *Client) AccountBlock(ctx context.Context, id string) (*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)
- if err != nil {
- return nil, err
- }
- return &relationship, nil
- }
- // AccountUnblock unblock the account.
- func (c *Client) AccountUnblock(ctx context.Context, id string) (*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)
- if err != nil {
- return nil, err
- }
- return &relationship, nil
- }
- // AccountMute mute the account.
- func (c *Client) AccountMute(ctx context.Context, id string, notifications bool, duration int) (*Relationship, error) {
- params := url.Values{}
- params.Set("notifications", strconv.FormatBool(notifications))
- params.Set("duration", strconv.Itoa(duration))
- var relationship Relationship
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%s/mute", url.PathEscape(string(id))), params, &relationship, nil)
- if err != nil {
- return nil, err
- }
- return &relationship, nil
- }
- // AccountUnmute unmute the account.
- func (c *Client) AccountUnmute(ctx context.Context, id string) (*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)
- if err != nil {
- return nil, err
- }
- return &relationship, nil
- }
- // GetAccountRelationships return relationship for the account.
- func (c *Client) GetAccountRelationships(ctx context.Context, ids []string) ([]*Relationship, error) {
- params := url.Values{}
- for _, id := range ids {
- params.Add("id[]", id)
- }
- var relationships []*Relationship
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/relationships", params, &relationships, nil)
- if err != nil {
- return nil, err
- }
- return relationships, nil
- }
- // 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)
- params.Set("limit", fmt.Sprint(limit))
- 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 send follow-request.
- func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, error) {
- params := url.Values{}
- params.Set("uri", uri)
- var account Account
- err := c.doAPI(ctx, http.MethodPost, "/api/v1/follows", params, &account, nil)
- if err != nil {
- return nil, err
- }
- return &account, nil
- }
- // 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)
- if err != nil {
- return nil, err
- }
- return accounts, nil
- }
- // FollowRequestAuthorize is authorize the follow request of user with id.
- func (c *Client) FollowRequestAuthorize(ctx context.Context, id string) error {
- return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/authorize", url.PathEscape(string(id))), nil, nil, nil)
- }
- // FollowRequestReject is rejects the follow request of user with id.
- func (c *Client) FollowRequestReject(ctx context.Context, id string) error {
- return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%s/reject", url.PathEscape(string(id))), nil, nil, nil)
- }
- // GetMutes returns the list of users muted by the current user.
- func (c *Client) GetMutes(ctx context.Context, pg *Pagination) ([]*Account, error) {
- var accounts []*Account
- err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts, pg)
- if err != nil {
- return nil, err
- }
- return accounts, nil
- }
- // Subscribe to receive notifications for all statuses posted by a user
- func (c *Client) Subscribe(ctx context.Context, id string) (*Relationship, error) {
- var relationship *Relationship
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/pleroma/accounts/%s/subscribe", url.PathEscape(id)), nil, &relationship, nil)
- if err != nil {
- return nil, err
- }
- return relationship, nil
- }
- // UnSubscribe to stop receiving notifications from user statuses
- func (c *Client) UnSubscribe(ctx context.Context, id string) (*Relationship, error) {
- var relationship *Relationship
- err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/pleroma/accounts/%s/unsubscribe", url.PathEscape(id)), nil, &relationship, nil)
- if err != nil {
- return nil, err
- }
- return relationship, nil
- }
- // GetBookmarks returns the list of bookmarked statuses
- 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
- }
|