summaryrefslogtreecommitdiffstats
path: root/lib/WWW/Pocket.pm
blob: 6567a9450b85599cbcda0c63b4b2cf0d7df17906 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
package WWW::Pocket;
use Moose;
# ABSTRACT: Wrapper for the Pocket v3 API

use HTTP::Tiny;
use IO::Socket::SSL; # Necessary for https URLs on HTTP::Tiny.
use JSON::PP;
use Carp;

=head1 SYNOPSIS

  use WWW::Pocket;

  my $pocket = WWW::Pocket->new(
      # get a consumer key at https://getpocket.com/developer/
      consumer_key => '...',
  );

  my ($url, $code) = $pocket->start_authentication('https://example.com/');
  # visit $url, log in
  $pocket->finish_authentication($code);

  say for map { $_->{resolved_url} } values %{ $pocket->retrieve->{list} };

=head1 DESCRIPTION

This module wraps the L<Pocket|https://getpocket.com/> v3 API. To use, you
must first authenticate via OAuth by providing a valid consumer key, and then
visiting the OAuth endpoint (handled by the C<start_authentication> and
C<finish_authentication> methods). Once logged in, you can interact with the
API via the C<add>, C<modify>, and C<retrieve> methods, which correspond to
the endpoints documented at L<https://getpocket.com/developer/docs/overview>.

This module also ships with a command line scripts called C<pocket>, for
interacting with the API from the command line. See the documentation for that
script for more details.

=cut

=attr consumer_key

The consumer key for your application. You can generate a consumer key at
L<https://getpocket.com/developer/apps/>. Required.

=cut

has consumer_key => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
);

=attr access_token

The token returned when you successfully authenticate. This can be used to
reauthenticate with the server without having to log in every time. It is set
automatically by C<finish_authentication>, but can also be provided directly
to avoid having to reauthenticate. It is required to be set before any API
methods are called.

=cut

has access_token => (
    is        => 'ro',
    isa       => 'Str',
    lazy      => 1,
    default   => sub { die "You must authenticate first." },
    predicate => 'has_access_token',
    writer    => '_set_access_token',
);

=attr username

The username that you have authenticated as. It is set automatically by
C<finish_authentication>, and can also be provided directly. It is
informational only.

=cut

has username => (
    is        => 'ro',
    isa       => 'Str',
    predicate => 'has_username',
    writer    => '_set_username',
);

=attr base_uri

The base URI for the Pocket service. Defaults to C<https://getpocket.com/>.

=cut

has base_uri => (
    is      => 'ro',
    isa     => 'Str',
    default => 'https://getpocket.com/',
);

=attr api_base_uri

The base URI for the API endpoints. Defaults to appending C</v3/> to the
C<base_uri>.

=cut

has api_base_uri => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub {
        my $base = $_[0]->base_uri;
        $base =~ s{/$}{};
        return "$base/v3/"
    },
);

=attr auth_base_uri

The base URI for the authentication endpoints. Defaults to appending C</auth/>
to the C<base_uri>.

=cut

has auth_base_uri => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub {
        my $base = $_[0]->base_uri;
        $base =~ s{/$}{};
        return "$base/auth/"
    },
);

=attr ua

The L<HTTP::Tiny> instance used to access the api.

=cut

has ua => (
    is      => 'ro',
    isa     => 'HTTP::Tiny',
    lazy    => 1,
    default => sub { HTTP::Tiny->new },
);

=method start_authentication($redirect_uri)

Call this method to begin the authentication process. You must provide the
C<$redirect_uri>, which is where the user will be redirected to after
authenticating. This method returns a URL and an authentication code. The user
must visit the URL to log into Pocket and approve the application, and then
you should call C<finish_authentication> with the authentication code after
that is done.

=cut

sub start_authentication {
    my $self = shift;
    my ($redirect_uri) = @_;

    return if $self->has_access_token;

    my $response = $self->_request(
        $self->api_base_uri . 'oauth/request',
        {
            consumer_key => $self->consumer_key,
            redirect_uri => $redirect_uri,
        },
    );
    return (
        $self->auth_base_uri . "authorize?request_token=$response->{code}&redirect_uri=$redirect_uri",
        $response->{code},
    );
}

=method finish_authentication($code)

Finishes the authentication process. Call this method with the code returned
by C<start_authentication> after the user has visited the URL which was also
returned by C<start_authentication>. Once this method returns, the
C<access_key> and C<username> attributes will be set, and other API methods
can be successfully called.

=cut

sub finish_authentication {
    my $self = shift;
    my ($code) = @_;

    my $response = $self->_request(
        $self->api_base_uri . 'oauth/authorize',
        {
            consumer_key => $self->consumer_key,
            code         => $code,
        },
    );

    $self->_set_access_token($response->{access_token});
    $self->_set_username($response->{username});

    return;
}

=method add(%params)

Wraps the L<add|https://getpocket.com/developer/docs/v3/add> endpoint.
C<%params> can include any parameters documented in the API documentation.

=cut

sub add {
    my $self = shift;
    my (%params) = @_;
    return $self->_endpoint_request('add', \%params);
}

=method modify

Wraps the L<modify|https://getpocket.com/developer/docs/v3/modify> endpoint.
C<%params> can include any parameters documented in the API documentation.

=cut

sub modify {
    my $self = shift;
    my (%params) = @_;
    return $self->_endpoint_request('send', \%params);
}

=method retrieve

Wraps the L<retrieve|https://getpocket.com/developer/docs/v3/retrieve>
endpoint. C<%params> can include any parameters documented in the API
documentation.

=cut

sub retrieve {
    my $self = shift;
    my (%params) = @_;
    return $self->_endpoint_request('get', \%params);
}

sub _endpoint_request {
    my $self = shift;
    my ($endpoint, $params) = @_;
    $params->{consumer_key} = $self->consumer_key;
    $params->{access_token} = $self->access_token;
    return $self->_request($self->api_base_uri . $endpoint, $params);
}

sub _request {
    my $self = shift;
    my ($uri, $params) = @_;

    my $response = $self->ua->post(
        $uri,
        {
            content => encode_json($params),
            headers => {
                'Content-Type' => 'application/json; charset=UTF-8',
                'X-Accept'     => 'application/json',
            },
        },
    );
    croak "Request for $uri failed ($response->{status}): $response->{content}"
        unless $response->{success};

    return decode_json($response->{content});
}

__PACKAGE__->meta->make_immutable;
no Moose;

=head1 BUGS

No known bugs.

Please report any bugs to GitHub Issues at
L<https://github.com/doy/www-pocket/issues>.

=head1 SEE ALSO

L<Webservice::Pocket> for the v2 API

=head1 SUPPORT

You can find this documentation for this module with the perldoc command.

    perldoc WWW::Pocket

You can also look for information at:

=over 4

=item * MetaCPAN

L<https://metacpan.org/release/WWW-Pocket>

=item * Github

L<https://github.com/doy/www-pocket>

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=WWW-Pocket>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/WWW-Pocket>

=back

=cut

1;