view release on metacpan or search on metacpan
lib/API/Instagram.pm view on Meta::CPAN
use API::Instagram::User;
use API::Instagram::Location;
use API::Instagram::Tag;
use API::Instagram::Media;
use API::Instagram::Media::Comment;
use API::Instagram::Search;
has client_id => ( is => 'ro', required => 1 );
has client_secret => ( is => 'ro', required => 1 );
has redirect_uri => ( is => 'ro', required => 1 );
has scope => ( is => 'ro', default => sub { 'basic' } );
has response_type => ( is => 'ro', default => sub { 'code' } );
has grant_type => ( is => 'ro', default => sub { 'authorization_code' } );
has code => ( is => 'rw', isa => sub { confess "Code not provided" unless $_[0] } );
has access_token => ( is => 'rw', isa => sub { confess "No access token provided" unless $_[0] } );
has no_cache => ( is => 'rw', default => sub { 0 } );
has _ua => ( is => 'ro', default => sub { Furl->new() } );
has _obj_cache => ( is => 'ro', default => sub { { User => {}, Media => {}, Location => {}, Tag => {}, 'Media::Comment' => {} } } );
has _endpoint_url => ( is => 'ro', default => sub { 'https://api.instagram.com/v1' } );
has _authorize_url => ( is => 'ro', default => sub { 'https://api.instagram.com/oauth/authorize' } );
has _access_token_url => ( is => 'ro', default => sub { 'https://api.instagram.com/oauth/access_token' } );
has _debug => ( is => 'rw', lazy => 1 );
my $instance;
sub BUILD { $instance = shift }
sub instance { $instance //= shift->new(@_) }
sub get_auth_url {
my $self = shift;
carp "User already authorized with code: " . $self->code if $self->code;
my @auth_fields = qw(client_id redirect_uri response_type scope);
for ( @auth_fields ) {
carp "ERROR: $_ required for generating authorization URL" and return unless defined $self->$_;
}
my $uri = URI->new( $self->_authorize_url );
$uri->query_form( map { $_ => $self->$_ } @auth_fields );
$uri->as_string();
}
sub get_access_token {
my $self = shift;
my @access_token_fields = qw(client_id redirect_uri grant_type client_secret code);
for ( @access_token_fields ) {
carp "ERROR: $_ required for generating access token." and return unless defined $self->$_;
}
my $data = { map { $_ => $self->$_ } @access_token_fields };
my $json = $self->_request( 'post', $self->_access_token_url, $data, { token_not_required => 1 } );
wantarray ? ( $json->{access_token}, $self->user( $json->{user} ) ) : $json->{access_token};
}
sub media { shift->_get_obj( 'Media', 'id', shift ) }
sub user { shift->_get_obj( 'User', 'id', shift // 'self' ) }
sub location { shift->_get_obj( 'Location', 'id', shift, 1 ) }
sub tag { shift->_get_obj( 'Tag', 'name', shift ) }
sub search {
my $self = shift;
my $type = shift;
API::Instagram::Search->new( type => $type )
}
sub popular_medias {
my $self = shift;
my $url = "/media/popular";
$self->_medias( $url, { @_%2?():@_ } );
}
sub _comment { shift->_get_obj( 'Media::Comment', 'id', shift ) }
#####################################################
# Returns cached wanted object or creates a new one #
#####################################################
sub _get_obj {
my ( $self, $type, $key, $code, $optional_code ) = @_;
my $data = { $key => $code };
$data = $code if ref $code eq 'HASH';
$code = $data->{$key};
# Returns if CODE is not optional and not defined or if it's not a string
return if (!$optional_code and !defined $code) or ref $code;
# Code used as cache key
lib/API/Instagram.pm view on Meta::CPAN
# Deletes cache if no-cache is set
delete $self->_cache($type)->{$cache_code} if $self->no_cache;
return $return;
}
###################################
# Returns a list of Media Objects #
###################################
sub _medias {
my ($self, $url, $params, $opts) = @_;
$params->{count} //= 33;
$params->{url} = $url;
[ map { $self->media($_) } $self->_get_list( { %$params, url => $url }, $opts ) ]
}
####################################################################
# Returns a list of the requested items. Does pagination if needed #
####################################################################
sub _get_list {
my $self = shift;
my $params = shift;
my $opts = shift;
my $url = delete $params->{url} || return [];
my $count = $params->{count} // 999_999_999;
$count = 999_999_999 if $count < 0;
$params->{count} = $count;
my $request = $self->_request( 'get', $url, $params, $opts );
lib/API/Instagram.pm view on Meta::CPAN
$request = $self->_request( 'get', $pagination->{next_url}, $params, $opts );
push @$data, @{ $request->{data} };
}
return @$data;
}
##############################################################
# Requests the data from the given URL with QUERY parameters #
##############################################################
sub _request {
my ( $self, $method, $url, $params, $opts ) = @_;
# Verifies access requirements
unless ( defined $self->access_token ) {
if ( !$opts->{token_not_required} or !defined $self->client_id ) {
carp "A valid access_token is required";
return {}
}
}
lib/API/Instagram.pm view on Meta::CPAN
# Verifies meta node
my $meta = $res->{meta};
carp "$meta->{error_type}: $meta->{error_message}" if $meta->{code} ne '200';
use Data::Dumper;
# die Dumper $res;
$res;
}
sub _request_data { shift->_request(@_)->{data} || {} }
sub _del { shift->_request_data( 'delete', @_ ) }
sub _get { shift->_request_data( 'get', @_ ) }
sub _post { shift->_request_data( 'post', @_ ) }
################################
# Returns requested cache hash #
################################
sub _cache { shift->_obj_cache->{ shift() } }
1;
__END__
=pod
=encoding UTF-8
lib/API/Instagram/Location.pm view on Meta::CPAN
use Moo;
use Carp;
has id => ( is => 'ro', predicate => 1 );
has latitude => ( is => 'lazy' );
has longitude => ( is => 'lazy' );
has name => ( is => 'lazy' );
has _data => ( is => 'rwp', lazy => 1, builder => 1, clearer => 1 );
sub recent_medias {
my $self = shift;
carp "Not available for location with no ID." and return [] unless $self->has_id;
my $url = sprintf "locations/%s/media/recent", $self->id;
API::Instagram->instance->_medias( $url, { @_%2?():@_ } );
}
sub _build_name { shift->_data->{name} }
sub _build_latitude { shift->_data->{latitude} }
sub _build_longitude { shift->_data->{longitude} }
sub _build__data {
my $self = shift;
carp "Not available for location with no ID." and return {} unless $self->has_id;
my $url = sprintf "locations/%s", $self->id;
API::Instagram->instance->_get( $url );
}
1;
__END__
lib/API/Instagram/Media.pm view on Meta::CPAN
has id => ( is => 'ro', required => 1 );
has type => ( is => 'lazy' );
has link => ( is => 'lazy' );
has filter => ( is => 'lazy' );
has images => ( is => 'lazy' );
has videos => ( is => 'lazy' );
has user => ( is => 'lazy', coerce => \&_coerce_user );
has tags => ( is => 'lazy', coerce => \&_coerce_tags );
has location => ( is => 'lazy', coerce => \&_coerce_location );
has users_in_photo => ( is => 'lazy', coerce => \&_coerce_users_in_photo );
has caption => ( is => 'lazy', coerce => sub { $_[0]->{text} if $_[0] and ref $_[0] eq 'HASH' } );
has created_time => ( is => 'lazy', coerce => sub { Time::Moment->from_epoch( $_[0] ) } );
has _api => ( is => 'lazy' );
has _data => ( is => 'rwp', lazy => 1, builder => 1, clearer => 1 );
sub likes {
my $self = shift;
$self->_clear_data if shift;
$self->_data->{likes}->{count}
}
sub last_likes {
my $self = shift;
$self->_clear_data if shift;
my $api = $self->_api;
[ map { $api->user($_) } @{ $self->_data->{likes}->{data} } ]
}
sub get_likes {
my $self = shift;
my %opts = @_;
my $url = sprintf "media/%s/likes", $self->id;
my $api = $self->_api;
[ map { $api->user($_) } $api->_get_list( { %opts, url => $url } ) ]
}
sub like {
my $self = shift;
my $url = sprintf "media/%s/likes", $self->id;
$self->_api->_post( $url )
}
sub dislike {
my $self = shift;
my $url = sprintf "media/%s/likes", $self->id;
$self->_api->_del( $url )
}
sub comments {
my $self = shift;
$self->_clear_data if shift;
$self->_data->{comments}->{count}
}
sub last_comments {
my $self = shift;
$self->_clear_data if shift;
my $api = $self->_api;
[ map { $api->_comment( { %$_, media => $self } ) } @{ $self->_data->{comments}->{data} } ]
}
sub get_comments {
my $self = shift;
my %opts = @_;
my $url = sprintf "media/%s/comments", $self->id;
my $api = $self->_api;
[ map { $api->_comment( { %$_, media => $self } ) } $api->_get_list( { %opts, url => $url } ) ]
}
sub comment {
my $self = shift;
my $text = shift;
my $url = sprintf "media/%s/comments", $self->id;
$self->_api->_post( $url, { text => $text } )
}
sub _build__api { API::Instagram->instance }
sub _build_user { shift->_data->{user} }
sub _build_tags { shift->_data->{tags} }
sub _build_location { shift->_data->{location} }
sub _build_users_in_photo { shift->_data->{users_in_photo} }
sub _build_type { shift->_data->{type} }
sub _build_link { shift->_data->{link} }
sub _build_filter { shift->_data->{filter} }
sub _build_images { shift->_data->{images} }
sub _build_videos { shift->_data->{videos} }
sub _build_caption { shift->_data->{caption } }
sub _build_created_time { shift->_data->{created_time} }
sub _build__data {
my $self = shift;
my $url = sprintf "media/%s", $self->id;
$self->_api->_get( $url );
}
############################################################
# Attributes coercion that API::Instagram object reference #
############################################################
sub _coerce_user { API::Instagram->instance->user ( $_[0] ) };
sub _coerce_location { API::Instagram->instance->location( $_[0] ) if $_[0] };
sub _coerce_tags {
my $data = $_[0];
return if ref $data ne 'ARRAY';
[ map { API::Instagram->instance->tag($_) } @$data ]
};
sub _coerce_users_in_photo {
my $data = $_[0];
return if ref $data ne 'ARRAY';
[
map {{
user => API::Instagram->instance->user( $_->{user} ),
position => $_->{position},
}} @$data
]
};
lib/API/Instagram/Media/Comment.pm view on Meta::CPAN
package API::Instagram::Media::Comment;
# ABSTRACT: Instagram Media Comment Object
use Moo;
use Time::Moment;
has id => ( is => 'ro', required => 1 );
has from => ( is => 'ro', required => 1, coerce => sub { API::Instagram->instance->user( $_[0] ) } );
has text => ( is => 'ro', required => 1 );
has media => ( is => 'ro', required => 1 );
has created_time => ( is => 'ro', coerce => sub { Time::Moment->from_epoch( $_[0] ) } );
sub remove {
my $self = shift;
my $url = sprintf "media/%s/comments/%s", $self->media->id, $self->id;
$self->media->_api->_del( $url )
}
1;
__END__
=pod
lib/API/Instagram/Search.pm view on Meta::CPAN
use Moo;
my $search = {
user => 'users/search',
media => 'media/search',
tag => 'tags/search',
location => 'locations/search',
};
has type => ( is => 'ro', required => 1, isa => sub { die "Type not supported." unless $search->{$_[0]} } );
sub find {
my $self = shift;
my %opts = @_;
my $type = $self->type;
my $url = $search->{$type};
my $api = API::Instagram->instance;
[ map { $api->$type($_) } $api->_get_list( { %opts, url => $url } ) ]
}
1;
lib/API/Instagram/Tag.pm view on Meta::CPAN
package API::Instagram::Tag;
# ABSTRACT: Instagram Tag Object
use Moo;
has name => ( is => 'ro', required => 1 );
has _data => ( is => 'rwp', lazy => 1, builder => 1, clearer => 1 );
sub media_count {
my $self = shift;
$self->_clear_data if shift;
$self->_data->{media_count}
}
sub recent_medias {
my $self = shift;
my $url = sprintf "tags/%s/media/recent", $self->name;
API::Instagram->instance->_medias( $url, { @_%2?():@_ } );
}
sub _build__data {
my $self = shift;
my $url = sprintf "tags/%s", $self->name;
API::Instagram->instance->_get( $url );
}
1;
__END__
=pod
lib/API/Instagram/User.pm view on Meta::CPAN
has id => ( is => 'ro', required => 1 );
has username => ( is => 'lazy' );
has full_name => ( is => 'lazy' );
has bio => ( is => 'lazy' );
has website => ( is => 'lazy' );
has profile_picture => ( is => 'lazy' );
has _api => ( is => 'lazy' );
has _data => ( is => 'rwp', lazy => 1, builder => 1, clearer => 1 );
sub media {
my $self = shift;
$self->_clear_data if shift;
return $_->{media} for $self->_data->{counts}
}
sub follows {
my $self = shift;
$self->_clear_data if shift;
return $_->{follows} for $self->_data->{counts}
}
sub followed_by {
my $self = shift;
$self->_clear_data if shift;
return $_->{followed_by} for $self->_data->{counts}
}
sub feed {
my $self = shift;
my @list = $self->_self_requests( 'feed', '/users/self/feed', @_ ) or return;
[ map { $self->_api->media($_) } @list ];
}
sub liked_media {
my $self = shift;
my @list = $self->_self_requests( 'liked-media', '/users/self/media/liked', @_ ) or return;
[ map { $self->_api->media($_) } @list ];
}
sub requested_by {
my $self = shift;
my @list = $self->_self_requests( 'requested-by', '/users/self/requested-by', @_ ) or return;
[ map { $self->_api->user($_) } @list ];
}
sub get_follows {
shift->_get_relashions( @_, relationship => 'follows' );
}
sub get_followers {
shift->_get_relashions( @_, relationship => 'followed-by' );
}
sub recent_medias {
my $self = shift;
my $url = sprintf "users/%s/media/recent", $self->id;
$self->_api->_medias( $url, { @_%2?():@_ }, { token_not_required => 1 } );
}
sub relationship {
my $self = shift;
my $action = shift;
my $url = sprintf "users/%s/relationship", $self->id;
my @actions = qw/ follow unfollow block unblock approve ignore/;
use experimental 'smartmatch';
if ( $action ) {
if ( $action ~~ @actions ){
return $self->_api->_post( $url, { action => $action } )
}
carp "Invalid action";
}
$self->_api->_get( $url );
}
sub _get_relashions {
my $self = shift;
my %opts = @_;
my $url = sprintf "users/%s/%s", $self->id, $opts{relationship};
my $api = $self->_api;
[ map { $api->user($_) } $api->_get_list( { %opts, url => $url } ) ]
}
sub _self_requests {
my ($self, $type, $url, %opts) = @_;
if ( $self->id ne $self->_api->user->id ){
carp "The $type is only available for the authenticated user";
return;
}
$self->_api->_get_list( { %opts, url => $url } )
}
sub BUILDARGS {
my $self = shift;
my $opts = shift;
$opts->{profile_picture} //= delete $opts->{profile_pic_url} if exists $opts->{profile_pic_url};
return $opts;
}
sub _build__api { API::Instagram->instance }
sub _build_username { shift->_data->{username} }
sub _build_full_name { shift->_data->{full_name} }
sub _build_bio { shift->_data->{bio} }
sub _build_website { shift->_data->{website} }
sub _build_profile_picture { shift->_data->{profile_picture} }
sub _build__data {
my $self = shift;
my $url = sprintf "users/%s", $self->id;
$self->_api->_get( $url );
}
1;
__END__
t/00-comment.t view on Meta::CPAN
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
no_cache => 1,
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
my $media = $api->media(123);
isa_ok( $media, 'API::Instagram::Media' );
my $get_comments = $media->get_comments;
is ref $get_comments, 'ARRAY';
my $comment = $get_comments->[0];
isa_ok( $comment, 'API::Instagram::Media::Comment' );
t/00-instagram.t view on Meta::CPAN
use Furl::Response;
use Inline::Files;
use API::Instagram;
use Test::More tests => 17;
my $data = join '', <DATA>;
my $ua = Test::MockObject::Extends->new( Furl->new() );
my $res = Test::MockObject::Extends->new( Furl::Response->new( 1, 200, 'OK', {}, $data) );
$ua->mock('get', sub { $res });
$ua->mock('post', sub { $res });
my $api = API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
no_cache => 1,
_ua => $ua,
});
t/00-instagram.t view on Meta::CPAN
is $me->username, "snoopdogg";
is ref $api->_request('get','media'), 'HASH';
my @list = $api->_get_list( { url => 'media', count => 2 } );
is ~~@list , 2;
# Tests Popular Medias method with new DATA (__POPULAR__)
my $popular = join '', <POPULAR>;
my $res2 = Test::MockObject::Extends->new( Furl::Response->new( 1, 200, 'OK', {}, $popular) );
$ua->mock('get', sub { $res2 });
is ref $api2->popular_medias, 'ARRAY';
is $api2->popular_medias->[0]->user->username, 'cocomiin';
__DATA__
{
"meta": {
"code": 200
},
"pagination": {
t/00-location.t view on Meta::CPAN
my $api = Test::MockObject::Extends->new(
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });
my $location = $api->location('1');
isa_ok( $location, 'API::Instagram::Location' );
is $location->id, 1;
is $location->name, 'Dogpatch Labs';
is $location->latitude, 37.782;
is $location->longitude, -122.387;
is ref $location->recent_medias, 'ARRAY';
t/00-media.t view on Meta::CPAN
use API::Instagram;
use Test::More tests => 27;
my $api = Test::MockObject::Extends->new(
API::Instagram->new({
client_id => '123', client_secret => '456', redirect_uri => 'http://localhost', no_cache => 1, })
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { {} });
# First Object
my $media = $api->media(3);
isa_ok( $media, 'API::Instagram::Media' );
is $media->id, 3;
is $media->type, 'video';
is $media->filter, 'Vesper';
is $media->likes, 1;
is $media->comments, 2;
t/00-search.t view on Meta::CPAN
my $api = Test::MockObject::Extends->new(
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
my $search = $api->search('tag');
isa_ok( $search, 'API::Instagram::Search' );
my $tags = $search->find( q => 'x' );
is ref $tags, 'ARRAY';
isa_ok( $tags->[0], 'API::Instagram::Tag' );
is $tags->[3]->name, 'snowydays';
my $api = Test::MockObject::Extends->new(
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });
my $tag = $api->tag('nofilter');
isa_ok( $tag, 'API::Instagram::Tag' );
is $tag->name, 'nofilter';
is $tag->media_count, 472;
is $tag->media_count(1), 472;
is ref $tag->recent_medias, 'ARRAY';
__DATA__
t/00-user.t view on Meta::CPAN
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
no_cache => 1
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });
my $user = $api->user("1574083");
isa_ok( $user, 'API::Instagram::User' );
is $user->id, 1574083;
is $user->username, 'snoopdogg';
is $user->full_name, 'Snoop Dogg';
is $user->bio, 'This is my bio';
is $user->website, 'http://snoopdogg.com';
is $user->profile_picture, 'http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg';
t/01-location.t view on Meta::CPAN
my $api = Test::MockObject::Extends->new(
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });
my $location = $api->location( $json->{data} );
isa_ok( $location, 'API::Instagram::Location' );
is $location->id, undef;
is $location->name, undef;
is ref $location->recent_medias, 'ARRAY';
__DATA__
{
t/01-media.t view on Meta::CPAN
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
no_cache => 1,
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });
my $media = $api->media( $json->{data} );
isa_ok( $media, 'API::Instagram::Media' );
is $media->location, undef;
is ref $media->tags, 'ARRAY';
is $media->tags->[0], undef;
my $uip = $media->users_in_photo;
is ref $uip, 'ARRAY';
t/01-search.t view on Meta::CPAN
my $api = Test::MockObject::Extends->new(
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
my $search = eval { $api->search('car') };
is $search, undef;
__DATA__
{
"data": [
{
"media_count": 43590,
"name": "snowy"
t/01-user.t view on Meta::CPAN
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
no_cache => 1
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });
my $user = $api->user( $json->{data} );
isa_ok( $user, 'API::Instagram::User' );
is $user->id, 'self';
is $user->profile_picture, 'http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg';
is ref $user->feed, 'ARRAY';
is ref $user->liked_media, 'ARRAY';
is ref $user->requested_by, 'ARRAY';
t/02-user.t view on Meta::CPAN
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
no_cache => 1
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });
my $user = $api->user( $json->{data} );
isa_ok( $user, 'API::Instagram::User' );
is $user->id, 123;
is $user->profile_picture, 'http://test.com/picture.jpg';
is $user->feed, undef;
is $user->liked_media, undef;
is $user->requested_by, undef;
t/03-user.t view on Meta::CPAN
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
no_cache => 1
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });
my $user = $api->user( $json->{data} );
isa_ok( $user, 'API::Instagram::User' );
is $user->profile_picture, undef;
__DATA__
{
"data": {
"id": "1574083",
t/04-user.t view on Meta::CPAN
API::Instagram->new({
client_id => '123',
client_secret => '456',
redirect_uri => 'http://localhost',
no_cache => 1
})
);
my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_post', sub { $json });
my $user = $api->user( 123 );
isa_ok( $user, 'API::Instagram::User' );
is ref $user->relationship, 'HASH';
is $user->relationship->{incoming_status}, 'requested_by';
is ref $user->relationship('block'), 'HASH';
is ref $user->relationship('undef'), 'HASH';
__DATA__