instagram

package module
v0.0.0-...-ac0f2f5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 19, 2015 License: MIT Imports: 7 Imported by: 0

README

golang-instagram

A instagram API wrapper.

Features

Implemented:

  • All GET requests are implemented
  • Both authenticated and unauthenticated requests can be made
  • A nice iterator facility for paginated requests
  • No interface{} data types! (1 exception, see Location.Id note below)

Todo:

  • Authentication
  • POST / DELETE requests (you need special permissions for these so no way to test.)

Documentation

Documentation on godoc.org

Install

go get github.com/azr/golang-instagram

Creation

import (
  "github.com/azr/golang-instagram"
)

unauthenticatedApi := &instagram.Api{
  ClientId: "my-client-id",
}

authenticatedApi := &instagram.Api{
  AccessToken: "my-access-token",
}

anotherAuthenticatedApi := instagram.New("", "my-access-token")

Usage

See the documentation, endpoint examples, and the iteration tests for a deeper dive than whats below.

import (
  "fmt"
  "github.com/azr/golang-instagram"
  "net/url"
)

func DoSomeInstagramApiStuff(accessToken string) {
  api := New("", accessToken)

  if ok, err := api.VerifyCredentials(); !ok {
    panic(err)
  }

  var myId string

  // Get yourself!
  if resp, err := api.GetSelf(); err != nil {
    panic(err)
  } else {
    // A response has two fields: Meta which you shouldn't really care about
    // And whatever your getting, in this case, a User
    me := resp.User
    fmt.Printf("My userid is %s and I have %d followers\n", me.Id, me.Counts.FollowedBy)
  }

  params := url.Values{}
  params.Set("count", "1")
  if resp, err := api.GetUserRecentMedia("self" /* this works :) */, params); err != nil {
    panic(err)
  } else {
    if len(resp.Medias) == 0 { // [sic]
      panic("I should have some sort of media posted on instagram!")
    }
    media := resp.Medias[0]
    fmt.Println("My last media was a %s with %d comments and %d likes. (url: %s)\n", media.Type, media.Comments.Count, media.Like.Count, media.Link)
  }
}

There's many more endpoints and a fancy iteration wrapper. Check it out in the code and documentation!

Iteration

So pagination makes iterating through a list of users or media possible, but its not easy. So, because Go has nice iteration facilities (i.e. range), this package includes two useful methods for iterating over paginating: api.IterateMedias and api.IterateUsers. You can see the tests and the docs for more info.

// First go and make the original request, passing in any additional parameters you need
res, err := api.GetUserRecentMedia("some-user", params)
if err != nil {
  panic(err)
}

// If you plan to break early, create a done channel. Pass in nil if you plan to exhaust the pagination
done := make(chan bool)
defer close(done)

// Here we get back two channels. Don't worry about the error channel for now
medias, errs := api.IterateMedia(res, done)

for media := range medias {
  processMedia(media)

  if doneWithMedia(media) {
    // This is how we signal to the iterator to quit early
    done <- true
  }
}

// If we exited early due to an error, we can check here
if err, ok := <- errs; ok && err != nil {
  panic(err)
}

Tests

To run the tests, you'll need at least a ClientId (which you can get from here), and preferably an authenticated users' AccessToken, which you can get from making a request on the API Console

First, fill in config_test.go.example and save it as config_test.go. Then run go test

Notes

  • Certain methods require an access token so check the official documentation before using an unauthenticated Api. Also, there is a 5000 request per hour rate limit on any one ClientId or AccessToken, so it is advisable to use AccessTokens when available. This package will use it if it is given over a ClientId.
  • Location.Id is sometimes returned as an integer (in media i think) and sometimes a string. Because of this, we have to call it an interface{}. But there is a facility to force it to a string, as follows:
var loc Location
stringIdVersion := instagram.ParseLocationId(loc.Id)

If anyone can prove to me that they fixed this bug, just let me know and we can change it to a string (all other IDs are strings...)

  • created_time fields come back as strings. So theres a handy type StringUnixTimeStringUnixTime which has a nice method func (sut StringUnixTime) Time() (time.Time, error) that you can use to cast it to a golang time.
  • I apologize for using Medias [sic] everywhere, I needed a plural version that isn't spelled the same.

License

MIT-style. See LICENSE file.

Documentation

Overview

Package instagram provides a minimialist instagram API wrapper.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrBodyAndUrlParamsSet = fmt.Errorf("Cannot merge query params in urlStr and params")
	ErrJsonDecode          = fmt.Errorf("instagram: error decoding body")
)

Functions

func ParseLocationId

func ParseLocationId(lid LocationId) string

Types

type Api

type Api struct {
	ClientId           string
	AccessToken        string
	RatelimitRemaining int
}

func New

func New(clientId string, accessToken string) *Api

Create an API with either a ClientId OR an accessToken. Only one is required. Access tokens are preferred because they keep rate limiting down.

Example

ExampleNew sets up the whole instagram API

apiWithoutAuthenticatedUser := New("client_key", "")
if _, err := apiWithoutAuthenticatedUser.GetMediaPopular(nil); err != nil {
	panic(err)
}
fmt.Println("Successfully created instagram.Api without user credentials")

apiAuthenticatedUser := New("", "access_token")
if ok, err := apiAuthenticatedUser.VerifyCredentials(); !ok {
	panic(err)
}
fmt.Println("Successfully created instagram.Api with user credentials")

func (*Api) GetLocation

func (api *Api) GetLocation(locationId string, params url.Values) (res *LocationResponse, err error)

Get information about a location. Gets /locations/{location-id}

func (*Api) GetLocationRecentMedia

func (api *Api) GetLocationRecentMedia(locationId string, params url.Values) (res *PaginatedMediasResponse, err error)

Get a list of recent media objects from a given location. May return a mix of both image and video types. Gets /locations/{location-id}/media/recent

func (*Api) GetLocationSearch

func (api *Api) GetLocationSearch(params url.Values) (res *LocationsResponse, err error)

Search for a location by geographic coordinate. Gets /locations/search

func (*Api) GetMedia

func (api *Api) GetMedia(mediaId string, params url.Values) (res *MediaResponse, err error)

Get information about a media object. The returned type key will allow you to differentiate between image and video media. Note: if you authenticate with an OAuth Token, you will receive the user_has_liked key which quickly tells you whether the current user has liked this media item. Gets /media/{media-id}

func (*Api) GetMediaComments

func (api *Api) GetMediaComments(mediaId string, params url.Values) (res *CommentsResponse, err error)

Get a full list of comments on a media. Required Scope: comments Gets /media/{media-id}/comments

func (*Api) GetMediaLikes

func (api *Api) GetMediaLikes(mediaId string, params url.Values) (res *UsersResponse, err error)

Get a list of users who have liked this media. Required Scope: likes Gets /media/{media-id}/likes

func (*Api) GetMediaPopular

func (api *Api) GetMediaPopular(params url.Values) (res *MediasResponse, err error)

Get a list of what media is most popular at the moment. Can return mix of image and video types. Gets /media/popular

Example

ExampleApi_GetMediaPopular shows how you can paginate through popular media

api := New("client_id" /* or */, "access_token")

mediasResponse, err := api.GetMediaPopular(nil)
if err != nil {
	panic(err)
}

for _, media := range mediasResponse.Medias {
	processMedia(&media)
}

func (*Api) GetMediaSearch

func (api *Api) GetMediaSearch(params url.Values) (res *MediasResponse, err error)

Search for media in a given area. The default time span is set to 5 days. The time span must not exceed 7 days. Defaults time stamps cover the last 5 days. Can return mix of image and video types. Gets /media/search

func (*Api) GetSelf

func (api *Api) GetSelf() (res *UserResponse, err error)

Get basic information about authenticated user. Gets /users/self

func (*Api) GetTag

func (api *Api) GetTag(tagName string, params url.Values) (res *TagResponse, err error)

Get information about a tag object. Gets /tags/{tag-name}

func (*Api) GetTagRecentMedia

func (api *Api) GetTagRecentMedia(tagName string, params url.Values) (res *PaginatedMediasResponse, err error)

Get a list of recently tagged media. Note that this media is ordered by when the media was tagged with this tag, rather than the order it was posted. Use the max_tag_id and min_tag_id parameters in the pagination response to paginate through these objects. Can return a mix of image and video types. Gets /tags/{tag-name}/media/recent

func (*Api) GetTagSearch

func (api *Api) GetTagSearch(params url.Values) (res *TagsResponse, err error)

Search for tags by name. Results are ordered first as an exact match, then by popularity. Short tags will be treated as exact matches. Gets /tags/search

func (*Api) GetUser

func (api *Api) GetUser(userId string, params url.Values) (res *UserResponse, err error)

Get basic information about a user. Gets /users/{user-id}

Example

ExampleApi_GetUser shows how to get a user object

api := New("client_id" /* or */, "access_token")

userResponse, err := api.GetUser("user-id", nil)
if err != nil {
	panic(err)
}

user := userResponse.User
processUser(user)

func (*Api) GetUserFeed

func (api *Api) GetUserFeed(params url.Values) (res *PaginatedMediasResponse, err error)

See the authenticated user's feed. May return a mix of both image and video types. Gets /users/self/feed

func (*Api) GetUserFollowedBy

func (api *Api) GetUserFollowedBy(userId string, params url.Values) (res *PaginatedUsersResponse, err error)

Get the list of users this user follows. Required Scope: relationships Gets /users/{user-id}/followed-by

func (*Api) GetUserFollows

func (api *Api) GetUserFollows(userId string, params url.Values) (res *PaginatedUsersResponse, err error)

Get the list of users this user follows. Required Scope: relationships Gets /users/{user-id}/follows

func (*Api) GetUserLikedMedia

func (api *Api) GetUserLikedMedia(params url.Values) (res *PaginatedMediasResponse, err error)

See the authenticated user's list of media they've liked. May return a mix of both image and video types. Note: This list is ordered by the order in which the user liked the media. Private media is returned as long as the authenticated user has permission to view that media. Liked media lists are only available for the currently authenticated user. Gets /users/self/media/liked

func (*Api) GetUserRecentMedia

func (api *Api) GetUserRecentMedia(userId string, params url.Values) (res *PaginatedMediasResponse, err error)

Get the most recent media published by a user. May return a mix of both image and video types. Gets /users/{user-id}/media/recent

func (*Api) GetUserRelationship

func (api *Api) GetUserRelationship(userId string, params url.Values) (res *RelationshipResponse, err error)

Get information about a relationship to another user. Required Scope: relationships Gets /users/{user-id}/relationship

func (*Api) GetUserRequestedBy

func (api *Api) GetUserRequestedBy(params url.Values) (res *UsersResponse, err error)

List the users who have requested this user's permission to follow. Required Scope: relationships Gets /users/self/requested-by

func (*Api) GetUserSearch

func (api *Api) GetUserSearch(params url.Values) (res *UsersResponse, err error)

Search for a user by name. Gets /users/search

func (*Api) IterateMedia

func (api *Api) IterateMedia(res *PaginatedMediasResponse, doneChan <-chan bool) (<-chan *Media, <-chan error)

IterateMedia makes pagination easy by converting the repeated api.NextMedias() call to a channel of media. Media will be passed in the reverse order of individual requests, for instance GetUserRecentMedia will go in reverse CreatedTime order. If you desire to break early, pass in a doneChan and close when you are breaking from iteration

Example

ExampleApi_IterateMedia shows how to use iteration on a channel to avoid the complex pagination calls

api := New("client_id" /* or */, "access_token")

mediasResponse, err := api.GetUserRecentMedia("user-id", nil)
if err != nil {
	panic(err)
}

// Stop 30 days ago
doneChan := make(chan bool)

mediaIter, errChan := api.IterateMedia(mediasResponse, doneChan /* optional */)
for media := range mediaIter {
	processMedia(media)

	if isDone(media) {
		close(doneChan) // Signal to iterator to quit
		break
	}
}

// When mediaIter is closed, errChan will either have a single error on it or it will have been closed so this is safe.
if err := <-errChan; err != nil {
	panic(err)
}

func (*Api) IterateUsers

func (api *Api) IterateUsers(res *PaginatedUsersResponse, doneChan <-chan bool) (<-chan *User, <-chan error)

IterateUsers makes pagination easy by converting the repeated api.NextUsers() call to a channel of users. Users will be passed in the reverse order of individual requests. If you desire to break early, pass in a doneChan and close when you are breaking from iteration

func (*Api) NextMedias

func (api *Api) NextMedias(mp *MediaPagination) (res *PaginatedMediasResponse, err error)

Get the next page of media

func (*Api) NextUsers

func (api *Api) NextUsers(up *UserPagination) (res *PaginatedUsersResponse, err error)

Get the next page of user

func (*Api) VerifyCredentials

func (api *Api) VerifyCredentials() (ok bool, err error)

Verify a valid client keys and user tokens by making a small request

type Attribution

type Attribution struct {
	Website   string
	ItunesUrl string
	Name      string
}

If another app uploaded the media, then this is the place it is given. As of 11/2013, Hipstamic is the only allowed app

type Caption

type Caption Comment

type Comment

type Comment struct {
	CreatedTime StringUnixTime `json:"created_time"`
	Text        string
	From        *User
	Id          string
}

type Comments

type Comments struct {
	Count int64
	Data  []Comment
}

type CommentsResponse

type CommentsResponse struct {
	MetaResponse
	Comments []Comment `json:"data"`
}

type Image

type Image struct {
	Url    string
	Width  int64
	Height int64
}

type Images

type Images struct {
	LowResolution      *Image `json:"low_resolution"`
	Thumbnail          *Image
	StandardResolution *Image `json:"standard_resolution"`
}

type Likes

type Likes struct {
	Count int64
	Data  []User
}

type Location

type Location struct {
	Id        LocationId
	Name      string
	Latitude  float64
	Longitude float64
}

type LocationId

type LocationId interface{}

Sometimes location Id is a string and sometimes its an integer

type LocationResponse

type LocationResponse struct {
	MetaResponse
	Location *Location `json:"data"`
}

type LocationsResponse

type LocationsResponse struct {
	MetaResponse
	Locations []Location `json:"data"`
}

type Media

type Media struct {
	Type         string
	Id           string
	UsersInPhoto []UserPosition `json:"users_in_photo"`
	Filter       string
	Tags         []string
	Comments     *Comments
	Caption      *Caption
	Likes        *Likes
	Link         string
	User         *User
	CreatedTime  StringUnixTime `json:"created_time"`
	Images       *Images
	Videos       *Videos
	Location     *Location
	UserHasLiked bool `json:"user_has_liked"`
	Attribution  *Attribution
}

Instagram Media object

type MediaPagination

type MediaPagination struct {
	*Pagination
}

MediaPagination will give you an easy way to request the next page of media.

type MediaResponse

type MediaResponse struct {
	MetaResponse
	Media *Media `json:"data"`
}

type MediasResponse

type MediasResponse struct {
	MetaResponse
	Medias []Media `json:"data"`
}

type Meta

type Meta struct {
	Code         int
	ErrorType    string `json:"error_type"`
	ErrorMessage string `json:"error_message"`
}

type MetaError

type MetaError Meta

func (*MetaError) Error

func (m *MetaError) Error() string

type MetaResponse

type MetaResponse struct {
	Meta *Meta
}

type PaginatedMediasResponse

type PaginatedMediasResponse struct {
	MediasResponse
	Pagination *MediaPagination
}

type PaginatedUsersResponse

type PaginatedUsersResponse struct {
	UsersResponse
	Pagination *UserPagination
}

type Pagination

type Pagination struct {
	NextUrl   string `json:"next_url"`
	NextMaxId string `json:"next_max_id"`

	// Used only on GetTagRecentMedia()
	NextMaxTagId string `json:"next_max_tag_id"`
	// Used only on GetTagRecentMedia()
	MinTagId string `json:"min_tag_id"`
}

func (*Pagination) NextPage

func (p *Pagination) NextPage() (done bool, uri string, params url.Values, err error)

Return the next page uri and parameters

type Position

type Position struct {
	X float64
	Y float64
}

A position in a media

type Relationship

type Relationship struct {
	IncomingStatus string `json:"incoming_status"`
	OutgoingStatus string `json:"outgoing_status"`
}

type RelationshipResponse

type RelationshipResponse struct {
	MetaResponse
	Relationship *Relationship `json:"data"`
}

type StringUnixTime

type StringUnixTime string

func (StringUnixTime) Time

func (s StringUnixTime) Time() (t time.Time, err error)

type Tag

type Tag struct {
	MediaCount int64 `json:"media_count"`
	Name       string
}

Instagram tag

type TagResponse

type TagResponse struct {
	MetaResponse
	Tag *Tag `json:"data"`
}

type TagsResponse

type TagsResponse struct {
	MetaResponse
	Tags []Tag `json:"data"`
}

type User

type User struct {
	Id             string
	Username       string
	FullName       string `json:"full_name"`
	ProfilePicture string `json:"profile_picture"`
	Bio            string
	Website        string
	Counts         *UserCounts
}

Instagram User Object. Note that user objects are not always fully returned. Be sure to see the descriptions on the instagram documentation for any given endpoint.

type UserCounts

type UserCounts struct {
	Media      int64
	Follows    int64
	FollowedBy int64 `json:"followed_by"`
}

Instagram User Counts object. Returned on User objects

type UserPagination

type UserPagination struct {
	*Pagination
}

UserPagination will give you an easy way to request the next page of media.

type UserPosition

type UserPosition struct {
	User     *User
	Position *Position
}

A pair of user object and position

type UserResponse

type UserResponse struct {
	MetaResponse
	User *User `json:"data"`
}

type UsersResponse

type UsersResponse struct {
	MetaResponse
	Users []User `json:"data"`
}

type Video

type Video Image

type Videos

type Videos struct {
	LowResolution      *Video `json:"low_resolution"`
	StandardResolution *Video `json:"standard_resolution"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL