diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b809561 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +# EditorConfig coding styles definitions. For more information about the +# properties used in this file, please see the EditorConfig documentation: +# http://editorconfig.org/ + +# indicate this is the root of the project +root = true + +[*] +charset = utf-8 + +end_of_line = LF +insert_final_newline = true +trim_trailing_whitespace = true + +indent_style = space +indent_size = 2 + +[Makefile] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +[*.go] +indent_style = tab \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..49b63e5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,20 @@ +# Automatically normalize line endings for all text-based files +# http://git-scm.com/docs/gitattributes#_end_of_line_conversion +* text=auto + +# For the following file types, normalize line endings to LF on checking and +# prevent conversion to CRLF when they are checked out (this is required in +# order to prevent newline related issues) +.* text eol=lf +*.go text eol=lf +*.yml text eol=lf +*.html text eol=lf +*.css text eol=lf +*.js text eol=lf +*.json text eol=lf +LICENSE text eol=lf + +# Exclude `website` and `cookbook` from GitHub's language statistics +# https://github.com/github/linguist#using-gitattributes +cookbook/* linguist-documentation +website/* linguist-documentation diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d29108e --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +.idea +.DS_Store +coverage.txt +_test +vendor +*.iml + +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..c972d3d --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,45 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/mitchellh/mapstructure" + packages = ["."] + revision = "d0303fe809921458f417bcf828397a65db30a7e4" + +[[projects]] + branch = "master" + name = "github.com/moul/http2curl" + packages = ["."] + revision = "4e24498b31dba4683efb9d35c1c8a91e2eda28c8" + +[[projects]] + name = "github.com/parnurzeal/gorequest" + packages = ["."] + revision = "a578a48e8d6ca8b01a3b18314c43c6716bb5f5a3" + version = "v0.2.15" + +[[projects]] + name = "github.com/pkg/errors" + packages = ["."] + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = ["idna","publicsuffix"] + revision = "f5079bd7f6f74e23c4d65efa0f4ce14cbd6a3c0f" + +[[projects]] + branch = "master" + name = "golang.org/x/text" + packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] + revision = "3bd178b88a8180be2df394a1fbb81313916f0e7b" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "78c1ce29bfe11072f0df8bbf318492beb06edb9d34c5c289cfec4c09027a73ff" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..1c4a533 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,30 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + branch = "master" + name = "github.com/mitchellh/mapstructure" + +[[constraint]] + name = "github.com/parnurzeal/gorequest" + version = "0.2.15" diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f67e962 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (C) 2017 Leonardo Esparis. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7cf42b2 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +

+

Gostagram

+

Unofficial and easy to use instagram client for go.

+

+ +--- + +###Quick Start. + +**First step** + +Go to instagram developer [website](https://www.instagram.com/developer/) +and create a developer account, then register a new instagram client. + +**Download and Installation** +```text +go get github.com/leoxnidas/gostagram +``` + +**Usage** + +Basic example, using an client to +consume an instagram endpoint. + +```go +package main + +import ( + "fmt" + "github.com/leoxnidas/gostagram" +) + +func main() { + client := gostagram.NewClient("access_token") + user, err := client.GetCurrentUser() + + if err != nil { + fmt.Println(err) + } else { + fmt.Println(user.Id) + fmt.Println(user.Username) + fmt.Println(user.FullName) + } +} +``` + +If you want to enable instagram signed requests mode +you have to tell gostagram client, that you want to sig +a request. + +```go +package main + +import ( + "fmt" + "github.com/leoxnidas/gostagram" +) + +func main() { + client := gostagram.NewClient("access_token") + client.SetSignedRequest(true) + client.SetClientSecret("client secret") + + + user, err := client.GetCurrentUser() + + if err != nil { + fmt.Println(err) + } else { + fmt.Println(user.Id) + fmt.Println(user.Username) + fmt.Println(user.FullName) + } +} +``` + +###Support us. + * [donate](https://www.paypal.me/leoxnidas). + * [Contribute](https://github.com/leoxnidas/gostagram#contribute). + * Talk about the project. + +###Contribute. +Please use [Github issue tracker](https://github.com/leoxnidas/gostagram/issues) +for everything. + * Report issues. + * Improve/Fix documentation. + * Suggest new features or enhancements. + +###License. +gostagram license [MIT](./LICENSE.txt) diff --git a/comments.go b/comments.go new file mode 100644 index 0000000..bdb3540 --- /dev/null +++ b/comments.go @@ -0,0 +1,88 @@ +package gostagram + +import ( + "fmt" + "errors" + "strings" + "regexp" + + "github.com/mitchellh/mapstructure" +) + +var ( + CommentsUrlExeed = errors.New("Cannot contain more than 1 URL.") + CommentsHashtagExceed = errors.New("Comment cannot contain more than 4 hashtags.") + CommentsMaxLengthExceed = errors.New("Comment cannot exceed 300 characters.") + CommentsCapitalLettersError = errors.New("Comment cannot consist of all capital letters") +) + +var ( + // take this pattern it from + // https://stackoverflow.com/questions/6883049/regex-to-find-urls-in-string-in-python + // to match an url. + urlMatcher = regexp.MustCompile(`http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+`) + capitalLetterMatcher = regexp.MustCompile(`[A-Z]`) +) + +type Comment struct { + From User + + Id string + Text string + CreatedTime string `mapstructure:"created_time"` +} + +func (c *Client) GetMediaComments(media_id string) ([]*Comment, error) { + tmp, _, err := c.get(fmt.Sprintf("%smedia/%s/comments?access_token=%s", apiUrl, media_id, c.access_token)) + if err != nil { + return nil, err + } + + tmpComments := (*tmp).([]interface{}) + var comments []*Comment + for _, tmpComment := range tmpComments { + var comment Comment + + if err := mapstructure.Decode(tmpComment, &comment); err != nil { + return nil, err + } + + comments = append(comments, &comment) + } + + return comments, nil +} + +func (c *Client) PostMediaComment(text, media_id string) error { + if len(text) > 300 { + return CommentsMaxLengthExceed + } else if strings.Count(text, "#") > 4 { + return CommentsHashtagExceed + } else if len(urlMatcher.FindAllSubmatch([]byte(text), -1)) > 1 { + return CommentsUrlExeed + } else { + for _, c := range text { + if capitalLetterMatcher.Match([]byte(string(c))) { + return CommentsCapitalLettersError + } + } + } + + _, err := c.post(fmt.Sprintf("%smedia/%s/comments?access_token=%s", apiUrl, media_id, c.access_token), BodyData{ + "text": text, + }) + + if err != nil { + return err + } + + return nil +} + +func (c *Client) DeleteMediaComment(media_id, comment_id string) error { + _, err := c.delete(fmt.Sprintf("%smedia/%s/comments/%s?access_token=%s", apiUrl, media_id, comment_id, c.access_token)) + if err != nil { + return err + } + return nil +} diff --git a/comments_test.go b/comments_test.go new file mode 100644 index 0000000..307bd1a --- /dev/null +++ b/comments_test.go @@ -0,0 +1,52 @@ +package gostagram + +import ( + "testing" +) + +func TestClient_GetMediaComments(t *testing.T) { + client := CreateClient(t) + + comments, err := client.GetMediaComments("1499806890266583125_2451237325") + + if err != nil { + t.Fatal(err) + } + + if len(comments) > 0 { + for _, comment := range comments { + t.Log("------------------ Start Comment ------------------") + t.Log("Id: ", DefaultStringValueIfEmpty(comment.Id)) + t.Log("Text: ", DefaultStringValueIfEmpty(comment.Text)) + t.Log("Created Time: ", DefaultStringValueIfEmpty(comment.CreatedTime)) + t.Log("------------------ Start User Comment ------------------") + LogUser(&comment.From, t) + t.Log("------------------ End User Comment ------------------") + t.Log("------------------ End Comment ------------------") + } + } else { + t.Log("no comments.") + } +} + +func TestClient_PostMediaComment(t *testing.T) { + client := CreateClient(t) + err := client.PostMediaComment("api text", "1499806890266583125_2451237325") + if err != nil { + t.Fatal(err) + } else { + t.Log("Comment posted.!") + } +} + +func TestClient_DeleteMediaComment(t *testing.T) { + client := CreateClient(t) + + err := client.DeleteMediaComment("1499806890266583125_2451237325", "17876627794082799") + + if err != nil { + t.Fatal(err) + } else { + t.Log("Comment deleted.") + } +} diff --git a/instagram.go b/instagram.go new file mode 100644 index 0000000..2accf81 --- /dev/null +++ b/instagram.go @@ -0,0 +1,183 @@ +package gostagram + +import ( + "fmt" + "errors" + "encoding/json" + url2 "net/url" + "strings" + + "github.com/parnurzeal/gorequest" +) + +const ( + apiUrl = "https://api.instagram.com/v1/" + version = "1.0.0-alpha1" + userAgent = "gostagram/v1" +) + +type Params map[string]string +type BodyData map[string]interface{} +type Response map[string]interface{} + +type Client struct { + clientSecret string + access_token string + + sandboxMode bool + signedRequest bool +} + +func NewClient(access_token string) *Client { + return &Client{ + clientSecret: "", + access_token: access_token, + signedRequest: false, + sandboxMode: false, + } +} + +func (c *Client) Version() string { + return version +} + +func (c *Client) SetClientSecret(cs string) { + c.clientSecret = cs +} + +func (c *Client) SetSignedRequest(sr bool) { + c.signedRequest = sr +} + +func (c *Client) SetSandboxMode(sm bool) { + c.sandboxMode = sm +} + +func (c *Client) newRequest(method, uri string, response *Response, dataToSend ...BodyData) error { + if c.signedRequest && len(c.clientSecret) == 0 { + return errors.New("Client secret not set.") + } else if c.signedRequest { + + tmpUrl, err := url2.Parse(uri) + + if err != nil { + return err + } + + endpoint := strings.Replace(tmpUrl.EscapedPath(), "/v1", "", 1) + + if endpoint[len(endpoint) - 1] == '/' { + endpoint = endpoint[0:len(endpoint) - 1] + } + + params := make(Params) + for paramName, paramValue := range tmpUrl.Query() { + params[paramName] = paramValue[0] + } + + sig, err := c.generateSignature(endpoint, params) + + if err != nil { + return err + } + + uri = uri + "&sig=" + sig + } + + request := gorequest.New().Set("User-Agent", userAgent) + + switch method { + case gorequest.POST: + request = request.Post(uri).Type("multipart") + + if dataToSend != nil && len(dataToSend) > 0 { + request = request.SendMap(dataToSend[0]) + } + + break + case gorequest.GET: + request = request.Get(uri).Type("json") + break + case gorequest.DELETE: + request = request.Delete(uri).Type("json") + break + } + + _, body, errs := request.EndBytes() + json.Unmarshal(body, &response) + + if len(errs) > 0 { + return errs[0] + } + + if len(*response) > 0 { + var code int + var errorType, errorMessage string + + if (*response)["meta"] != nil { + meta := (*response)["meta"].(map[string]interface{}) + code = int(meta["code"].(float64)) + + if code != 200 { + errorType = meta["error_type"].(string) + errorMessage = meta["error_message"].(string) + } else { + errorType = "" + errorMessage = "" + } + } else { + code = int((*response)["code"].(float64)) + errorType = (*response)["error_type"].(string) + errorMessage = (*response)["error_message"].(string) + } + + if code != 200 { + return errors.New(fmt.Sprintf("[%s]: %s", errorType, errorMessage)) + } + } else { + return errors.New("Wrong endpoint.") + } + + return nil +} + +func (c *Client) get(url string) (*interface{}, *interface{}, error) { + var response Response + err := c.newRequest(gorequest.GET, url, &response) + + if err != nil { + return nil, nil, err + } + + tmp := response["data"] + tmpPagination := response["pagination"] + if tmpPagination != nil { + return &tmp, nil, nil + } + + return &tmp, &tmpPagination, nil +} + +func (c *Client) post(url string, dataToSend BodyData) (*interface{}, error) { + var response Response + err := c.newRequest(gorequest.POST, url, &response, dataToSend) + + if err != nil { + return nil, err + } + + tmp := response["data"] + return &tmp, nil +} + +func (c *Client) delete(url string) (*interface{}, error) { + var response Response + err := c.newRequest(gorequest.DELETE, url, &response) + + if err != nil { + return nil, err + } + + tmp := response["data"] + return &tmp, nil +} diff --git a/likes.go b/likes.go new file mode 100644 index 0000000..9971446 --- /dev/null +++ b/likes.go @@ -0,0 +1,24 @@ +package gostagram + +import "fmt" + +func (c *Client) GetMediaLikes(media_id string) ([]*User, error) { + return c.getUsers(fmt.Sprintf("%smedia/%s/likes?access_token=%s", apiUrl, media_id, c.access_token)) +} + +func (c *Client) PostMediaLike(media_id string) error { + _, err := c.post(fmt.Sprintf("%smedia/%s/likes?access_token=%s", apiUrl, media_id, c.access_token), nil) + if err != nil { + return err + } + + return nil +} + +func (c *Client) DeleteMediaLike(media_id string) error { + _, err := c.delete(fmt.Sprintf("%smedia/%s/likes?access_token=%s", apiUrl, media_id, c.access_token)) + if err != nil { + return err + } + return nil +} diff --git a/likes_test.go b/likes_test.go new file mode 100644 index 0000000..e3eeb69 --- /dev/null +++ b/likes_test.go @@ -0,0 +1,42 @@ +package gostagram + +import "testing" + +func TestClient_GetMediaLikes(t *testing.T) { + client := CreateClient(t) + users, err := client.GetMediaLikes("1499806890266583125_2451237325") + + if err != nil { + t.Fatal(err) + } + + if len(users) > 0 { + for _, user := range users { + t.Log("------------------ Start User ------------------") + LogUser(user, t) + t.Log("------------------ End User ------------------") + } + } else { + t.Log("Not found any user.!") + } +} + +func TestClient_PostMediaLike(t *testing.T) { + client := CreateClient(t) + err := client.PostMediaLike("1499806890266583125_2451237325") + if err != nil { + t.Fatal(err) + } + + t.Log("media photo liked.") +} + +func TestClient_DeleteMediaLike(t *testing.T) { + client := CreateClient(t) + err := client.DeleteMediaLike("1499806890266583125_2451237325") + if err != nil { + t.Fatal(err) + } + + t.Log("media photo disliked.") +} \ No newline at end of file diff --git a/location.go b/location.go new file mode 100644 index 0000000..11d8108 --- /dev/null +++ b/location.go @@ -0,0 +1,56 @@ +package gostagram + +import ( + "fmt" + "github.com/mitchellh/mapstructure" +) + +type Location struct { + Id string + Name string + Latitude float64 + Longitude float64 +} + +func (c *Client) GetLocationById(location_id string) (*Location, error) { + tmp, _, err := c.get(fmt.Sprintf("%slocations/%s?access_token=%s", apiUrl, location_id, c.access_token)) + if err != nil { + return nil, err + } + + tmpLocation := (*tmp).(map[string]interface{}) + var location Location + if err = mapstructure.Decode(tmpLocation, &location); err != nil { + return nil, err + } + + return &location, nil +} + +func GetLocationOfRecentMedia(max_id, min_id int, access_token string) { + +} + +func (c *Client) SearchLocations(latitude, longitude, distance, facebook_places_id string) ([]*Location, error) { + tmp, _, err := c.get(fmt.Sprintf("%slocations/search?lat=%s&lng=%s&distance=%s&facebook_places_id=%s&access_token=%s", + apiUrl, latitude, longitude, distance, facebook_places_id, c.access_token)) + + if err != nil { + return nil, err + } + + tmpLocations := (*tmp).([]interface{}) + + var locations []*Location + for _, tmplocation := range tmpLocations { + var location Location + + if err := mapstructure.Decode(tmplocation, &location); err != nil { + return nil, err + } + + locations = append(locations, &location) + } + + return locations, nil +} diff --git a/location_test.go b/location_test.go new file mode 100644 index 0000000..1b2489d --- /dev/null +++ b/location_test.go @@ -0,0 +1,36 @@ +package gostagram + +import "testing" + +func TestClient_GetLocationById(t *testing.T) { + client := CreateClient(t) + tmp, err := client.GetLocationById("1898148680457787") + + if err != nil { + t.Fatal(err) + } + + t.Log(tmp.Name) + t.Log(tmp.Id) + t.Log(tmp.Longitude) + t.Log(tmp.Latitude) +} + +func TestClient_SearchLocations(t *testing.T) { + client := CreateClient(t) + tmp, err := client.SearchLocations("12.12", "23.1", "12312", "12312") + + if err != nil { + t.Fatal(err) + } + + for _, location := range tmp { + t.Log("------------------ Start Location ------------------") + t.Log(location.Name) + t.Log(location.Id) + t.Log(location.Longitude) + t.Log(location.Latitude) + t.Log("------------------ End Location ------------------") + } + +} diff --git a/media.go b/media.go new file mode 100644 index 0000000..ea47f90 --- /dev/null +++ b/media.go @@ -0,0 +1,221 @@ +package gostagram + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" +) + +type MediaType uint8 + +func (mt MediaType) IsImage() bool { + return mt == imageMediaType +} + +func (mt MediaType) IsVideo() bool { + return mt == videoMediaType +} + +func (mt MediaType) IsCarousel() bool { + return mt == videoMediaType +} + +const ( + imageMediaType MediaType = 1 + videoMediaType MediaType = 2 + carouselMediaType MediaType = 3 +) + +type Media interface { + MediaType() MediaType +} + +type Image struct { + Url string + Width int + Height int +} + +type VideoResolution struct { + Url string + Width int + Height int +} + +type BaseMediaResource struct { + Id string + Type string + Link string + Filter string + CreatedTime string `mapstructure:"created_time"` + + User User + UserHasLiked bool `mapstructure:"user_has_liked"` + Attribution interface{} + Tags []interface{} + + UserInPhoto []struct { + User User + + Position struct { + X float64 + Y float64 + } + } `mapstructure:"user_in_photo"` + + Comments struct { + Count int + } + + Caption struct { + From User + Id string + Text string + CreatedTime string + } + + Likes struct { + Count int + } + + Images struct { + Thumbnail Image + LowResolution Image `mapstructure:"low_resolution"` + StandardResolution Image `mapstructure:"standard_resolution"` + } + + Location struct { + Id string + Name string + Latitude float64 + Longitude float64 + StreetAddress string `mapstructure:"street_address"` + } +} + +type MediaImage struct { + BaseMediaResource `mapstructure:",squash"` +} + +func (mi MediaImage) MediaType() MediaType { + return imageMediaType +} + +type MediaVideo struct { + BaseMediaResource `mapstructure:",squash"` + + Videos struct { + LowResolution VideoResolution `mapstructure:"low_resolution"` + StandardResolution VideoResolution `mapstructure:"standard_resolution"` + } +} + +func (mi MediaVideo) MediaType() MediaType { + return videoMediaType +} + +type MediaCarousel struct { + BaseMediaResource `mapstructure:",squash"` + + CarouselMedia []struct{} +} + +func (mi MediaCarousel) MediaType() MediaType { + return carouselMediaType +} + +func (c *Client) getMedia(uri string) ([]*Media, error) { + tmp, _, err := c.get(uri) + + if err != nil { + return nil, err + } + + var tmpMediaArray []interface{} + switch (*tmp).(type) { + case []interface{}: + tmpMediaArray = (*tmp).([]interface{}) + break + case map[string]interface{}: + tmpMediaArray = append(tmpMediaArray, (*tmp).(map[string]interface{})) + break + } + + var media_array []*Media + for _, tmpMedia := range tmpMediaArray { + tmp := tmpMedia.(map[string]interface{}) + mediaType := tmp["type"].(string) + if mediaType == "image" { + if tmp["carousel_media"] != nil { + + } else { + var media MediaImage + + if err := mapstructure.Decode(tmpMedia, &media); err != nil { + return nil, err + } + + tt := Media(media) + media_array = append(media_array, &tt) + } + } else if mediaType == "video" { + var media MediaVideo + + if err := mapstructure.Decode(tmpMedia, &media); err != nil { + return nil, err + } + + tt := Media(media) + media_array = append(media_array, &tt) + } + } + + return media_array, nil +} + +func (c *Client) getOnlyOneMediaContent(uri string) (*Media, error) { + media, err := c.getMedia(uri) + if err != nil { + return nil, err + } + + return media[0], nil +} + +func (c *Client) GetCurrentUserRecentMedia(max, min, count int) ([]*Media, error) { + return c.getMedia(fmt.Sprintf("%susers/self/media/recent/?max_id=%d&min_id=%d&count=%d&access_token=%s", + apiUrl, max, min, count, c.access_token, + )) +} + +func (c *Client) GetUserMedia(user_id string, max, min, count int, ) ([]*Media, error) { + return c.getMedia(fmt.Sprintf("%susers/%s/media/recent/?max_id=%d&min_id=%d&count=%d&access_token=%s", + apiUrl, user_id, max, min, count, c.access_token, + )) +} + +func (c *Client) GetCurrentUserMediaLiked(max_like_id string, count int) ([]*Media, error) { + return c.getMedia(fmt.Sprintf("%susers/self/media/liked?max_like_id=%s&count=%d&access_token=%s", + apiUrl, max_like_id, count, c.access_token, + )) +} + +func (c *Client) GetMediaById(media_id string) (*Media, error) { + return c.getOnlyOneMediaContent(fmt.Sprintf("%smedia/%s?access_token=%s", apiUrl, media_id, c.access_token)) +} + +func (c *Client) GetMediaByShortcode(short_code string) (*Media, error) { + return c.getOnlyOneMediaContent(fmt.Sprintf("%smedia/shortcode/%s?access_token=%s", apiUrl, short_code, c.access_token)) +} + +func (c *Client) SearchMedia(lat, long float64) ([]*Media, error) { + return c.getMedia(fmt.Sprintf("%smedia/search?lat=%f&lng=%f&access_token=%s", apiUrl, lat, long, c.access_token)) +} + +func (c *Client) GetRecentMediaTaggedByTagName(tagname string) ([]*Media, error) { + return c.getMedia(fmt.Sprintf("%stags/%s/media/recent?access_token=%s", apiUrl, tagname, c.access_token)) +} + +func (c *Client) GetRecentMediaLocation(location_id string) ([]*Media, error) { + return c.getMedia(fmt.Sprintf("%slocations/%s/media/recent?access_token=%s", apiUrl, location_id, c.access_token)) +} diff --git a/media_test.go b/media_test.go new file mode 100644 index 0000000..dde715f --- /dev/null +++ b/media_test.go @@ -0,0 +1,232 @@ +package gostagram + +import "testing" + +func LogMedia(media *Media, t *testing.T) { + if (*media).MediaType().IsImage() { + mediaImage := (*media).(MediaImage) + + t.Log("------------------ Start Image Media ------------------") + t.Log("Id: ", DefaultStringValueIfEmpty(mediaImage.Id)) + t.Log("Type: ", DefaultStringValueIfEmpty(mediaImage.Type)) + t.Log("Link: ", DefaultStringValueIfEmpty(mediaImage.Link)) + t.Log("Filter: ", DefaultStringValueIfEmpty(mediaImage.Filter)) + t.Log("Created Time: ", DefaultStringValueIfEmpty(mediaImage.CreatedTime)) + + t.Log("------------------ Media Start User ------------------") + LogUser(&mediaImage.User, t) + t.Log("------------------ Media End User ------------------") + + t.Log("User Has Liked: ", mediaImage.UserHasLiked) + t.Log("Attribution: ", mediaImage.Attribution) + t.Log("Tags: ", mediaImage.Tags) + + t.Log("User in Photo: ") + for _, tmp := range mediaImage.UserInPhoto { + t.Log("User in Photo User: ") + LogUser(&tmp.User, t) + t.Log("User In Photo Position X: ", tmp.Position.X) + t.Log("User In Photo Position Y: ", tmp.Position.Y) + } + + t.Log("Comments Count: ", mediaImage.Comments.Count) + t.Log("Caption Id: ", DefaultStringValueIfEmpty(mediaImage.Caption.Id)) + t.Log("Caption Created Time: ", DefaultStringValueIfEmpty(mediaImage.Caption.CreatedTime)) + t.Log("Caption Created Text: ", DefaultStringValueIfEmpty(mediaImage.Caption.Text)) + t.Log("Caption User: ") + LogUser(&mediaImage.Caption.From, t) + t.Log("Likes Count: : ", mediaImage.Likes.Count) + + t.Log("------------------ Start Thumbnail Videos ------------------") + LogImage(&mediaImage.Images.Thumbnail, t) + t.Log("------------------ End Thumbnail Videos ------------------") + + t.Log("------------------ Start Low Resolution Image ------------------") + LogImage(&mediaImage.Images.LowResolution, t) + t.Log("------------------ End Low Resolution Image ------------------") + + t.Log("------------------ Start Standard Resolution Image ------------------") + LogImage(&mediaImage.Images.StandardResolution, t) + t.Log("------------------ End Standard Resolution Image ------------------") + + t.Log("Location Id: ", DefaultStringValueIfEmpty(mediaImage.Location.Id)) + t.Log("Location Name: ", DefaultStringValueIfEmpty(mediaImage.Location.Name)) + t.Log("Location Latitude: ", mediaImage.Location.Latitude) + t.Log("Location Longitude: ", mediaImage.Location.Longitude) + t.Log("Location Street Address: ", DefaultStringValueIfEmpty(mediaImage.Location.StreetAddress)) + + t.Log("------------------ End Image Media ------------------") + + } else if (*media).MediaType().IsVideo() { + mediaVideo := (*media).(MediaVideo) + + t.Log("------------------ Start Video Media ------------------") + t.Log("Id: ", DefaultStringValueIfEmpty(mediaVideo.Id)) + t.Log("Type: ", DefaultStringValueIfEmpty(mediaVideo.Type)) + t.Log("Link: ", DefaultStringValueIfEmpty(mediaVideo.Link)) + t.Log("Filter: ", DefaultStringValueIfEmpty(mediaVideo.Filter)) + t.Log("Created Time: ", DefaultStringValueIfEmpty(mediaVideo.CreatedTime)) + + t.Log("------------------ Media Start User ------------------") + LogUser(&mediaVideo.User, t) + t.Log("------------------ Media End User ------------------") + + t.Log("User Has Liked: ", mediaVideo.UserHasLiked) + t.Log("Attribution: ", mediaVideo.Attribution) + t.Log("Tags: ", mediaVideo.Tags) + + t.Log("User in Photo: ") + for _, tmp := range mediaVideo.UserInPhoto { + t.Log("User in Photo User:") + LogUser(&tmp.User, t) + t.Log("User In Photo Position X: ", tmp.Position.X) + t.Log("User In Photo Position Y: ", tmp.Position.Y) + } + + t.Log("Comments Count: ", mediaVideo.Comments.Count) + t.Log("Caption Id: ", DefaultStringValueIfEmpty(mediaVideo.Caption.Id)) + t.Log("Caption Created Time: ", DefaultStringValueIfEmpty(mediaVideo.Caption.CreatedTime)) + t.Log("Caption Created Text: ", DefaultStringValueIfEmpty(mediaVideo.Caption.Text)) + t.Log("Caption User: ") + LogUser(&mediaVideo.Caption.From, t) + t.Log("Likes Count: : ", mediaVideo.Likes.Count) + + t.Log("------------------ Start Thumbnail Videos ------------------") + LogImage(&mediaVideo.Images.Thumbnail, t) + t.Log("------------------ End Thumbnail Videos ------------------") + + t.Log("------------------ Start Low Resolution Image ------------------") + LogImage(&mediaVideo.Images.LowResolution, t) + t.Log("------------------ End Low Resolution Image ------------------") + + t.Log("------------------ Start Standard Resolution Image ------------------") + LogImage(&mediaVideo.Images.StandardResolution, t) + t.Log("------------------ End Standard Resolution Image ------------------") + + t.Log("Location Id: ", DefaultStringValueIfEmpty(mediaVideo.Location.Id)) + t.Log("Location Name: ", DefaultStringValueIfEmpty(mediaVideo.Location.Name)) + t.Log("Location Latitude: ", mediaVideo.Location.Latitude) + t.Log("Location Longitude: ", mediaVideo.Location.Longitude) + t.Log("Location Street Address: ", DefaultStringValueIfEmpty(mediaVideo.Location.StreetAddress)) + + t.Log("------------------ Start Standard Resolution Videos ------------------") + LogVideo(&mediaVideo.Videos.StandardResolution, t) + t.Log("------------------ End Standard Resolution Videos ------------------") + + t.Log("------------------ End Start Resolution Videos ------------------") + LogVideo(&mediaVideo.Videos.LowResolution, t) + t.Log("------------------ End Low Resolution Videos ------------------") + + t.Log("------------------ End Video Media ------------------") + } +} + +func LogImage(image *Image, t *testing.T) { + t.Log("Image Url: ", DefaultStringValueIfEmpty(image.Url)) + t.Log("Image Height: ", image.Height) + t.Log("Image Width: ", image.Width) +} + +func LogVideo(video *VideoResolution, t *testing.T) { + t.Log("Video Url: ", DefaultStringValueIfEmpty(video.Url)) + t.Log("Video Width: ", video.Width) + t.Log("Video Height: ", video.Height) +} + +func IterateMedia(media_arr []*Media, t *testing.T) { + if len(media_arr) > 0 { + for _, media := range media_arr { + LogMedia(media, t) + } + } else { + t.Log("No content.") + } +} + +func TestClient_GetCurrentUserRecentMedia(t *testing.T) { + client := CreateClient(t) + media_arr, err := client.GetCurrentUserRecentMedia(1,1,1) + + if err != nil { + t.Fatal(err) + } + + IterateMedia(media_arr, t) +} + +func TestClient_GetUserMedia(t *testing.T) { + client := CreateClient(t) + media_arr, err := client.GetUserMedia("3066964584", 1,1,1) + + if err != nil { + t.Fatal(err) + } + + IterateMedia(media_arr, t) +} + +func TestClient_GetCurrentUserMediaLiked(t *testing.T) { + client := CreateClient(t) + media_arr, err := client.GetCurrentUserMediaLiked("1499806890266583125_2451237325", 1) + + if err != nil { + t.Fatal(err) + } + + IterateMedia(media_arr, t) +} + +func TestClient_GetMediaById(t *testing.T) { + client := CreateClient(t) + media, err := client.GetMediaById("1499806890266583125_2451237325") + + if err != nil { + t.Fatal(err) + } + + LogMedia(media, t) +} + +func TestClient_GetMediaByShortcode(t *testing.T) { + client := CreateClient(t) + media, err := client.GetMediaByShortcode("BTQYmueBeBV") + + if err != nil { + t.Fatal(err) + } + + LogMedia(media, t) +} + +func TestClient_SearchMedia(t *testing.T) { + client := CreateClient(t) + media_arr, err := client.SearchMedia(0, 0) + + if err != nil { + t.Fatal(err) + } + + IterateMedia(media_arr, t) +} + +func TestClient_GetRecentMediaTaggedByTagName(t *testing.T) { + client := CreateClient(t) + media_arr, err := client.GetRecentMediaTaggedByTagName("leoxnidas") + + if err != nil { + t.Fatal(err) + } + + IterateMedia(media_arr, t) +} + +func TestClient_GetRecentMediaLocation(t *testing.T) { + client := CreateClient(t) + media_arr, err := client.GetRecentMediaLocation("1898148680457787") + + if err != nil { + t.Fatal(err) + } + + IterateMedia(media_arr, t) +} diff --git a/relationship.go b/relationship.go new file mode 100644 index 0000000..67c7745 --- /dev/null +++ b/relationship.go @@ -0,0 +1,91 @@ +package gostagram + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" +) + +type Relationship struct { + OutgoingStatus string `mapstructure:"outgoing_status"` + IncomingStatus string `mapstructure:"incoming_status"` +} + +func (r Relationship) Follows() bool { + return r.OutgoingStatus == "follows" +} + +func (r Relationship) Requested() bool { + return r.OutgoingStatus == "requested" +} + +func (r Relationship) FollowedBy() bool { + return r.IncomingStatus == "followed_by" +} + +func (r Relationship) RequestedBy() bool { + return r.IncomingStatus == "requested_by" +} + +func (r Relationship) BlockedByYou() bool { + return r.IncomingStatus == "blocked_by_you" +} + + +func (c *Client) doRelationshipAction(action, uri string) (*Relationship, error) { + tmp, err := c.post(uri, BodyData{ + "action": action, + }) + + if err != nil { + return nil, err + } + + tmpRel := (*tmp).(map[string]interface{}) + var relationship Relationship + if err = mapstructure.Decode(tmpRel, &relationship); err != nil { + return nil, err + } + + return &relationship, nil +} + +func (c *Client) GetCurrentUserRelationship(user_id string) (*Relationship, error) { + tmp, _, err := c.get(fmt.Sprintf("%susers/%s/relationship?access_token=%s", apiUrl, user_id, c.access_token)) + if err != nil { + return nil, err + } + + tmpRel := (*tmp).(map[string]interface{}) + var relationship Relationship + if err = mapstructure.Decode(tmpRel, &relationship); err != nil { + return nil, err + } + + return &relationship, nil +} + +func (c *Client) FollowUserById(id string) (*Relationship, error) { + return c.doRelationshipAction("follow", fmt.Sprintf("%susers/%s/relationship?access_token=%s", apiUrl, id, c.access_token)) +} + +func (c *Client) UnFollowUserById(id string) (*Relationship, error) { + return c.doRelationshipAction("unfollow", fmt.Sprintf("%susers/%s/relationship?access_token=%s", apiUrl, id, c.access_token)) +} + +func (c *Client) ApproveUserById(id string) (*Relationship, error) { + return c.doRelationshipAction("approve", fmt.Sprintf("%susers/%s/relationship?access_token=%s", apiUrl, id, c.access_token)) +} + +func (c *Client) IgnoreUserById(id string) (*Relationship, error) { + return c.doRelationshipAction("ignore", fmt.Sprintf("%susers/%s/relationship?access_token=%s", apiUrl, id, c.access_token)) +} + +func (c *Client) BlockUserById(id string) (*Relationship, error) { + return c.doRelationshipAction("block", fmt.Sprintf("%susers/%s/relationship?access_token=%s", apiUrl, id, c.access_token)) +} + +func (c *Client) UnBlockUserById(id string) (*Relationship, error) { + return c.doRelationshipAction("unblock", fmt.Sprintf("%susers/%s/relationship?access_token=%s", apiUrl, id, c.access_token)) +} + diff --git a/relationship_test.go b/relationship_test.go new file mode 100644 index 0000000..d3027e8 --- /dev/null +++ b/relationship_test.go @@ -0,0 +1,87 @@ +package gostagram + +import "testing" + +func TestClient_GetCurrentUserRelationship(t *testing.T) { + client := CreateClient(t) + tmp, err := client.GetCurrentUserRelationship("3066964584") + + if err != nil { + t.Fatal(err) + } + + t.Log(DefaultStringValueIfEmpty(tmp.IncomingStatus)) + t.Log(DefaultStringValueIfEmpty(tmp.OutgoingStatus)) +} + +func TestClient_UnFollowUserById(t *testing.T) { + client := CreateClient(t) + tmp, err := client.UnFollowUserById("3066964584") + + if err != nil { + t.Fatal(err) + } + + t.Log(DefaultStringValueIfEmpty(tmp.IncomingStatus)) + t.Log(DefaultStringValueIfEmpty(tmp.OutgoingStatus)) +} + +func TestClient_FollowUserById(t *testing.T) { + client := CreateClient(t) + tmp, err := client.FollowUserById("3066964584") + + if err != nil { + t.Fatal(err) + } + + t.Log(DefaultStringValueIfEmpty(tmp.IncomingStatus)) + t.Log(DefaultStringValueIfEmpty(tmp.OutgoingStatus)) +} + +func TestClient_BlockUserById(t *testing.T) { + client := CreateClient(t) + tmp, err := client.BlockUserById("3066964584") + + if err != nil { + t.Fatal(err) + } + + t.Log(DefaultStringValueIfEmpty(tmp.IncomingStatus)) + t.Log(DefaultStringValueIfEmpty(tmp.OutgoingStatus)) +} + +func TestClient_UnBlockUserById(t *testing.T) { + client := CreateClient(t) + tmp, err := client.UnBlockUserById("3066964584") + + if err != nil { + t.Fatal(err) + } + + t.Log(DefaultStringValueIfEmpty(tmp.IncomingStatus)) + t.Log(DefaultStringValueIfEmpty(tmp.OutgoingStatus)) +} + +func TestClient_IgnoreUserById(t *testing.T) { + client := CreateClient(t) + tmp, err := client.IgnoreUserById("3066964584") + + if err != nil { + t.Fatal(err) + } + + t.Log(DefaultStringValueIfEmpty(tmp.IncomingStatus)) + t.Log(DefaultStringValueIfEmpty(tmp.OutgoingStatus)) +} + +func TestClient_ApproveUserById(t *testing.T) { + client := CreateClient(t) + tmp, err := client.ApproveUserById("3066964584") + + if err != nil { + t.Fatal(err) + } + + t.Log(DefaultStringValueIfEmpty(tmp.IncomingStatus)) + t.Log(DefaultStringValueIfEmpty(tmp.OutgoingStatus)) +} diff --git a/signature.go b/signature.go new file mode 100644 index 0000000..2163262 --- /dev/null +++ b/signature.go @@ -0,0 +1,24 @@ +package gostagram + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "fmt" +) + +func (c *Client) generateSignature(endpoint string, params Params) (string, error) { + sig := endpoint + for key, val := range params { + sig += fmt.Sprintf("|%s=%s", key, val) + } + + + tmp := hmac.New(sha256.New, []byte(c.clientSecret)) + _, err := tmp.Write([]byte(sig)) + if err != nil { + return "", err + } + + return hex.EncodeToString(tmp.Sum(nil)), nil +} diff --git a/tags.go b/tags.go new file mode 100644 index 0000000..e47e3b8 --- /dev/null +++ b/tags.go @@ -0,0 +1,49 @@ +package gostagram + +import ( + "fmt" + "github.com/mitchellh/mapstructure" +) + +type Tag struct { + Name string + MediaCount int `mapstructure:"media_count"` +} + +func (c *Client) GetTagByName(tagname string) (*Tag, error) { + tmp, _, err := c.get(fmt.Sprintf("%stags/%s?access_token=%s", apiUrl, tagname, c.access_token)) + + if err != nil { + return nil, err + } + + tmpTag := (*tmp).(map[string]interface{}) + var tag Tag + if err = mapstructure.Decode(tmpTag, &tag); err != nil { + return nil, err + } + + return &tag, nil +} + +func (c *Client) SearchTags(query string) ([]*Tag, error) { + tmp, _, err := c.get(fmt.Sprintf("%stags/search?q=%s&access_token=%s", apiUrl, query, c.access_token)) + if err != nil { + return nil, err + } + + tmpUsers := (*tmp).([]interface{}) + + var tags []*Tag + for _, tmpUser := range tmpUsers { + var tag Tag + + if err := mapstructure.Decode(tmpUser, &tag); err != nil { + return nil, err + } + + tags = append(tags, &tag) + } + + return tags, nil +} diff --git a/tags_test.go b/tags_test.go new file mode 100644 index 0000000..9719910 --- /dev/null +++ b/tags_test.go @@ -0,0 +1,33 @@ +package gostagram + +import ( + "testing" +) + +func TestClient_GetTagByName(t *testing.T) { + client := CreateClient(t) + tmp, err := client.GetTagByName("leoxnidas") + + if err != nil { + t.Fatal(err) + } + + t.Log(tmp.Name) + t.Log(tmp.MediaCount) +} + +func TestClient_SearchTags(t *testing.T) { + client := CreateClient(t) + tmp, err := client.SearchTags("leoxn") + + if err != nil { + t.Fatal(err) + } + + for _, tag := range tmp { + t.Log("------------------ Start Tag ------------------") + t.Log(tag.Name) + t.Log(tag.MediaCount) + t.Log("------------------ End Tag ------------------") + } +} \ No newline at end of file diff --git a/users.go b/users.go new file mode 100644 index 0000000..acfaa0f --- /dev/null +++ b/users.go @@ -0,0 +1,96 @@ +package gostagram + +import ( + "fmt" + "github.com/mitchellh/mapstructure" +) + +type UserDetailed struct { + Id string + Bio string + Website string + Username string + FullName string `mapstructure:"full_name"` + ProfilePicture string `mapstructure:"profile_picture"` + + Counts struct { + Media int + Follows int + FollowedBy int `mapstructure:"followed_by"` + } +} + +type User struct { + Id string + Type string + Username string + LastName string `mapstructure:"last_name"` + FirstName string `mapstructure:"first_name"` + ProfilePicture string `mapstructure:"profile_picture"` +} + +func (c *Client) getUser(uri string) (*UserDetailed, error) { + tmp, _, err := c.get(uri) + + if err != nil { + return nil, err + } + + userDetailedMap := (*tmp).(map[string]interface{}) + var userDetailed UserDetailed + if err = mapstructure.Decode(userDetailedMap, &userDetailed); err != nil { + return nil, err + } + + return &userDetailed, nil +} + +func (c *Client) getUsers(uri string) ([]*User, error) { + tmp, _, err := c.get(uri) + if err != nil { + return nil, err + } + + tmpUsers := (*tmp).([]interface{}) + + var users []*User + for _, tmpUser := range tmpUsers { + var user User + + if err := mapstructure.Decode(tmpUser, &user); err != nil { + return nil, err + } + + users = append(users, &user) + } + + return users, nil +} + +func (c *Client) GetCurrentUser() (*UserDetailed, error) { + return c.getUser(fmt.Sprintf("%susers/self/?access_token=%s", apiUrl, c.access_token)) +} + +func (c *Client) GetUser(id string) (*UserDetailed, error) { + return c.getUser(fmt.Sprintf("%susers/%s/?access_token=%s", apiUrl, id, c.access_token)) +} + +func (c *Client) SearchUsers(query string, count int) ([]*User, error) { + return c.getUsers(fmt.Sprintf("%susers/search?q=%s&count=%d&access_token=%s", + apiUrl, query, count, c.access_token)) +} + +func (c *Client) GetCurrentUserFollows() ([]*User, error) { + return c.getUsers(fmt.Sprintf("%susers/self/follows?access_token=%s", + apiUrl, c.access_token)) +} + +func (c *Client) GetCurrentUserFollowedBy() ([]*User, error) { + return c.getUsers(fmt.Sprintf("%susers/self/followed-by?access_token=%s", + apiUrl, c.access_token)) +} + +func (c *Client) GetCurrentUserRequestedBy() ([]*User, error) { + return c.getUsers(fmt.Sprintf("%susers/self/requested-by?access_token=%s", + apiUrl, c.access_token)) +} diff --git a/users_test.go b/users_test.go new file mode 100644 index 0000000..a90781b --- /dev/null +++ b/users_test.go @@ -0,0 +1,180 @@ +package gostagram + +import ( + "testing" + "os" +) + +var ( + clientSecret = "94637a21fdab42dca158c6f6bd1fbe19" + accessToken = "2451237325.e0f323b.e8ab7baadad945e0856bebcae2032127" +) + +func GetAccessTokenFromEnv() string { + return os.Getenv("ACCESS_TOKEN") +} + +func GetClientSecret() string { + return os.Getenv("CLIENT_SECRET") +} + +func IsSecureRequest() bool { + return os.Getenv("SECURE") == "true" +} + +func GetUserId() string { + return os.Getenv("USER_ID") +} + +func CreateClient(t *testing.T) *Client { + //accessToken := GetAccessTokenFromEnv() + //if len(accessToken) == 0 { + // t.Error("Access token not set.") + //} + + client := NewClient(accessToken) + + //if IsSecureRequest() { + // client.SetSignedRequest(true) + // client_secret := GetClientSecret() + // if len(client_secret) == 0 { + // t.Error("Cannot send secure request without client secret") + // } else { + // client.SetClientSecret(client_secret) + // } + //} + + client.SetSignedRequest(true) + client.SetClientSecret(clientSecret) + + return client +} + +func DefaultStringValueIfEmpty(t string) string { + if len(t) > 0 { + return t + } + + return "None" +} + +func LogUserDetailed(user *UserDetailed, t *testing.T) { + t.Log("------------------ Start User ------------------") + t.Log("Username: ", DefaultStringValueIfEmpty(user.Username)) + t.Log("Profile Picture: ", DefaultStringValueIfEmpty(user.ProfilePicture)) + t.Log("Fullname: ", DefaultStringValueIfEmpty(user.FullName)) + t.Log("Website: ", DefaultStringValueIfEmpty(user.Website)) + t.Log("Bio: ", DefaultStringValueIfEmpty(user.Bio)) + t.Log("Id: ", DefaultStringValueIfEmpty(user.Id)) + t.Log("Followed by: ", user.Counts.FollowedBy) + t.Log("Follows: ", user.Counts.Follows) + t.Log("Media: ", user.Counts.Media) + t.Log("------------------ End User ------------------") +} + +func LogUser(user *User, t *testing.T) { + t.Log("Username: ", DefaultStringValueIfEmpty(user.Username)) + t.Log("Profile Picture: ", DefaultStringValueIfEmpty(user.ProfilePicture)) + t.Log("First Name: ", DefaultStringValueIfEmpty(user.FirstName)) + t.Log("Last Name: ", DefaultStringValueIfEmpty(user.LastName)) + t.Log("Id: ", DefaultStringValueIfEmpty(user.Id)) + t.Log("Type: ", DefaultStringValueIfEmpty(user.Type)) +} + +func TestClient_GetCurrentUser(t *testing.T) { + client := CreateClient(t) + tmp, err := client.GetCurrentUser() + + if err != nil { + t.Fatal(err) + } + + LogUserDetailed(tmp, t) +} + +func TestClient_GetUser(t *testing.T) { + client := CreateClient(t) + tmp, err := client.GetUser("3066964584") + + if err != nil { + t.Fatal(err) + } + + LogUserDetailed(tmp, t) +} + +func TestClient_SearchUsers(t *testing.T) { + client := CreateClient(t) + users, err := client.SearchUsers("leo", 1) + + if err != nil { + t.Fatal(err) + } + + if len(users) > 0 { + for _, user := range users { + t.Log("------------------ Start User ------------------") + LogUser(user, t) + t.Log("------------------ End User ------------------") + } + } else { + t.Log("Not found any user.!") + } +} + +func TestClient_GetCurrentUserFollows(t *testing.T) { + client := CreateClient(t) + users, err := client.GetCurrentUserFollows() + + if err != nil { + t.Fatal(err) + } + + if len(users) > 0 { + for _, user := range users { + t.Log("------------------ Start User ------------------") + LogUser(user, t) + t.Log("------------------ End User ------------------") + } + } else { + t.Log("Not found any user.!") + } +} + +func TestClient_GetCurrentUserFollowedBy(t *testing.T) { + client := CreateClient(t) + users, err := client.GetCurrentUserFollowedBy() + + if err != nil { + t.Fatal(err) + } + + if len(users) > 0 { + for _, user := range users { + t.Log("------------------ Start User ------------------") + LogUser(user, t) + t.Log("------------------ End User ------------------") + } + } else { + t.Log("Nothing found.!") + } +} + +func TestClient_GetCurrentUserRequestedBy(t *testing.T) { + client := CreateClient(t) + users, err := client.GetCurrentUserRequestedBy() + + if err != nil { + t.Fatal(err) + } + + if len(users) > 0 { + for _, user := range users { + t.Log("------------------ Start User ------------------") + LogUser(user, t) + t.Log("------------------ End User ------------------") + } + } else { + t.Log("Not found any user.!") + } +}