TaskWarrior tells me:

šŸ’» āœ” jacoby@oz 16:56 83Ā°F ļƒ“ ļƒ“  ~
$ task +dropbox
[task next ( +dropbox )]

ID Age Tag     Description                              Urg
 5 4mo dropbox sync full directory with Dropbox via API 1.61

1 task

I do not have a strong memory as to why Past Dave thought that was important, but oh well.

I did get to the point where, using WebService::Dropbox, I could upload and download files, but thatā€™s where I left it.

I mean, I get it. My tendency is to ln -s Dropbox/bin bin and lib and a few others, so I keep my collection of tools around where I want them, with only a quick sync away from everything, should I rewrite my sudoku solver or the thing that randomly tweets a picture of Snoopy playing saxophone. Yā€™know, the important things.

And, there are some places I run that I run headless without permissions and canā€™t really install the full Dropbox setup. Maybe my lib/?

Anyway, lack of urgent fires lead to me cleaning out my TaskWarrior tasks, and I noticed the above, and decided to finish it. It is not clean, but I havenā€™t posted to the blog recently enough, so here goes.

#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use feature qw{ postderef say signatures state };
no warnings qw{ experimental::postderef experimental::signatures };

use Carp;
use File::Path qw{make_path};
use Getopt::Long;
use IO::File;
use Pod::Usage;
use WebService::Dropbox;
use YAML qw{LoadFile};

my $json    = JSON->new->canonical->pretty;
my $config  = config();
my $dropbox = WebService::Dropbox->new(
	{
		key    => $config->{key},
		secret => $config->{secret},
	}
);

# Authorization
if ( $config->{token} ) {
	$dropbox->access_token( $config->{token} );
}else {
	my $url = $dropbox->authorize;

	print "Please Access URL and press Enter: $url\n";
	print "Please Input Code: ";
	chomp( my $code = <STDIN> );
	unless ( $dropbox->token($code) ) {
		die $dropbox->error;
	}
	print "Successfully authorized.\nYour AccessToken: ",$dropbox->access_token, "\n";
}

if ( $config->{directory} ) {
	my $remote = '/' . $config->{directory};
	get_dir( $remote, $dropbox );
}

sub get_dir( $remote, $dropbox ) {
	my $local = join '', $ENV{HOME}, '/Dropbox', $remote;
	if ( !-d $local ) { make_path($local) }
	my $result = $dropbox->list_folder($remote);
	for my $e ( $result->{entries}->@* ) {
		if ( $e->{'.tag'} eq 'folder' ) {
			my $next = $e->{path_display};
			get_dir( $next, $dropbox );
		}
		if ( $e->{'.tag'} eq 'file' ) {
			my $file = $e->{path_display};
			get_file( $file, $dropbox );
		}
	}
}

sub get_file( $remote, $dropbox ) {
	my $local = join '', $ENV{HOME}, '/Dropbox', $remote;
	say $remote ;
	say $local ;
	say '';
	my $fh = IO::File->new( $local, '>' );
	my $response = $dropbox->download( $remote, $fh );
	say $json->encode($response);
}

exit;

sub config () {
	my $config_file = $ENV{HOME} . '/.dropbox.yml';
	croak 'No Config' unless -f $config_file;
	my $config = LoadFile($config_file);
	$config->{download} = 0;
	$config->{upload}   = 0;
	GetOptions(
		'help'        => \$config->{help},
		'man'         => \$config->{man},
		'directory=s' => \$config->{directory},
	);

	pod2usage( -verbose => 2, -exitval => 1 ) if $config->{man};
	pod2usage( -verbose => 1, -exitval => 1 ) if $config->{help};
	pod2usage( -verbose => 1, -exitval => 1 )
		unless $config->{directory} =~ /\w/;
	delete $config->{help};
	delete $config->{man};
	return $config;
}

exit;

=head1 NAME

dropbox_copy.pl -- This documentation is for last time and needs updating so clipping


=head1 LICENSE

This is released under the Artistic
License. See L<perlartistic>.

=head1 AUTHOR

Dave Jacoby L<jacobydavid@live.com>

=cut

The hard part of using web APIs like this, I find, is token management. Once you get that, itā€™s using LWP or Mojo::UserAgent or curl or whatever, but the task of getting permissions in the first place is always what hinders me. I recall the process under WebService::Dropbox being easy.

I feel I should note the modules that make this easy

Carp is a module that supplants die and warn, due to antisocial behavior from those commands. Those I kinda use because I know itā€™s the Perly way.

File::Path gives make_path, which is essentially mkdir -p without shelling out. Similarly, IO::File is a thing I normally handle with open my $fh, '>', $file or whatever, but the module suggested this and I like well enough.

There are many ways to get options, but Getopt::Long is in Core and is my favorite.

JSON and YAML are kinda pairs, and I use YAML to store config data and JSON to handle the output of APIs. The cool kids are moving to Cpanel::JSON::XS, which I often use as well.

Perl uses POD, or Plain Old Documentation as their default user documentation, and Pod::Usage allows me to tie it to -h and -m, allowing me to easily create both short-form and long-form user documentation, for when I go back after years and wonder ā€œWhat is this and why am I running it?ā€

Observant readers will notice that the task says sync while this simply copies from Dropbox. This might be fixed, but it might not.

If you have any questions or comments, I would be glad to hear it. Ask me on Twitter or make an issue on my blog repo.