#!/usr/bin/env perl
|
|
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
pinboard - get some stuff from a pinboard account
|
|
|
|
=head1 USAGE
|
|
|
|
F<~/.pinboardrc> must contain a username in the format C<username:token>.
|
|
|
|
# Show some recent stuff from today (empty if nothing):
|
|
pinboard
|
|
|
|
# List all tags:
|
|
pinboard tags
|
|
|
|
# Everything for a specific tag:
|
|
pinboard by-tag some-tag
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
A wrapper for some Pinboard API calls I use routinely.
|
|
|
|
Pretty crap.
|
|
|
|
=head1 AUTHOR
|
|
|
|
L<Brennen Bearnes|https://p1k3.com/>
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use 5.10.0;
|
|
|
|
use Carp;
|
|
use Data::Dumper;
|
|
use File::HomeDir;
|
|
use Getopt::Long;
|
|
use LWP::Simple;
|
|
use Pod::Usage;
|
|
use JSON;
|
|
use Time::Piece;
|
|
|
|
use utf8;
|
|
use open qw(:std :utf8);
|
|
|
|
# Handle options, including help generated from the POD above
|
|
# (there's nothing else here at the moment, but it's a good place
|
|
# to hang things if I want options):
|
|
GetOptions(
|
|
'help' => sub { pod2usage(0) },
|
|
) or pod2usage(2);
|
|
|
|
if (defined $ARGV[0] && $ARGV[0] eq 'tags') {
|
|
print tags();
|
|
} elsif (defined $ARGV[0] && $ARGV[0] eq 'by-tag') {
|
|
croak "Must supply a tag name"
|
|
unless defined $ARGV[1];
|
|
say format_posts(by_tag($ARGV[1]));
|
|
} else {
|
|
my $today = localtime;
|
|
say format_posts(recent($today));
|
|
}
|
|
|
|
sub tags {
|
|
my $token = get_token();
|
|
|
|
my $url = 'https://api.pinboard.in/v1/tags/get?format=json&auth_token=' . $token;
|
|
my $pinboard_json = get($url);
|
|
|
|
# https://stackoverflow.com/questions/45941522/parsing-utf-8-json-with-perl
|
|
# Strip byte order mark:
|
|
# $pinboard_json =~ s/^\N{BOM}//g;
|
|
$pinboard_json =~ s/^[^\x00-\x7f]+//;
|
|
|
|
my $JSON = JSON->new->utf8->pretty;
|
|
my $pinboard_hashref = $JSON->decode($pinboard_json);
|
|
my %tags = %{ $pinboard_hashref };
|
|
my $output = '';
|
|
foreach my $tag (keys %tags) {
|
|
$output .= "$tag\n";
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
sub by_tag {
|
|
my $tag = shift;
|
|
my $token = get_token();
|
|
|
|
# lol this is terrible
|
|
my $url = 'https://api.pinboard.in/v1/posts/all?tag='
|
|
. $tag
|
|
. '&format=json&auth_token=' . $token;
|
|
my $pinboard_json = get($url);
|
|
# print $url;
|
|
# print $pinboard_json;
|
|
my $JSON = JSON->new->utf8->pretty;
|
|
my $pinboard_hashref = $JSON->decode($pinboard_json);
|
|
|
|
return @{ $pinboard_hashref };
|
|
}
|
|
|
|
sub recent {
|
|
my ($day) = @_;
|
|
|
|
my $token = get_token();
|
|
|
|
my $url = $day->strftime(
|
|
'https://api.pinboard.in/v1/posts/get?dt=%Y-%m-%d&meta=yes&format=json&auth_token='
|
|
. $token
|
|
);
|
|
|
|
my $pinboard_json = get($url);
|
|
my $JSON = JSON->new->utf8->pretty;
|
|
my $pinboard_hashref = $JSON->decode($pinboard_json);
|
|
return @{ $pinboard_hashref->{posts} };
|
|
}
|
|
|
|
# Take a list of posts and format them for display
|
|
sub format_posts {
|
|
my @output;
|
|
foreach my $post (@_)
|
|
{
|
|
# push @output, Dumper($post);
|
|
push @output,
|
|
"$post->{time}\t$post->{href}\t$post->{description}\t$post->{tags}";
|
|
}
|
|
|
|
if (@output > 0) {
|
|
return join "\n", @output;
|
|
}
|
|
return;
|
|
}
|
|
|
|
# PHP-style file-content grabbing:
|
|
sub file_get_contents {
|
|
my ($file) = @_;
|
|
|
|
open my $fh, '<', $file
|
|
or croak "Couldn't open $file: $!\n";
|
|
|
|
my $contents;
|
|
{
|
|
# line separator:
|
|
local $/ = undef;
|
|
$contents = <$fh>;
|
|
}
|
|
|
|
close $fh or croak "Couldn't close $file: $!";
|
|
|
|
return $contents;
|
|
}
|
|
|
|
sub get_token {
|
|
# See the bit about auth here:
|
|
# https://pinboard.in/api/
|
|
my $home = File::HomeDir->my_home;
|
|
my $rc_path = "${home}/.pinboardrc";
|
|
my $token;
|
|
if (-e $rc_path) {
|
|
$token = file_get_contents($rc_path);
|
|
} else {
|
|
die("No $rc_path found - do you need to create a file with credentials?");
|
|
}
|
|
chomp($token);
|
|
|
|
return $token;
|
|
}
|