WebSer...Dropbox(3pm)
πΈ WebService::Dropbox(3pm) πΈ
okay bestie, the manual page name up top is WebService::Dropbox(3pm), and yeah the header squishes it down to WebSer...Dropbox(3pm) on both sides because the full name is a whole mouthful and manpages are dramatic like that. It lives in the User Contributed Perl Documentation zone. We're doing this in three parts, and this is part one, so buckle up. π«Ά
β¨ NAME β¨
WebService::Dropbox β it's a Perl interface to the Dropbox API. That's the whole vibe. π
π SYNOPSIS π
here's the little taste test, no cap:
use WebService::Dropbox;
my $dropbox = WebService::Dropbox->new({
key => '...', # App Key
secret => '...' # App Secret
});
# Authorization
if ($access_token) {
$dropbox->access_token($access_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";
}
my $info = $dropbox->get_current_account or die $dropbox->error;
# download
# https://www.dropbox.com/developers/documentation/http/documentation#files-download
my $fh_download = IO::File->new('some file', '>');
$dropbox->download('/make_test_folder/test.txt', $fh_download) or die $dropbox->error;
$fh_get->close;
# upload
# https://www.dropbox.com/developers/documentation/http/documentation#files-upload
my $fh_upload = IO::File->new('some file');
$dropbox->upload('/make_test_folder/test.txt', $fh_upload) or die $dropbox->error;
$fh_upload->close;
# get_metadata
# https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata
my $data = $dropbox->get_metadata('/folder_a');
like, it's giving auth then do stuff, and honestly? iconic. π
π DESCRIPTION π
WebService::Dropbox is a Perl interface to the Dropbox API. fr fr, that's the point of it.
and here's what it brings to the table:
- Support Dropbox v2 REST API β¨
- Support Furl (Fast!!!) ποΈ
- Streaming IO (Low Memory) π§
lowkey obsessed with that last one, your RAM stays unbothered.
π API v1 => v2 Migration guide
π Migration API
so the mapping is like: files => download, files_put => upload, ...etc β you get the pattern, bestie.
π https://www.dropbox.com/developers/reference/migration-guide
π Migration OAuth1 Token => OAuth2 Token
got an old OAuth1 token lying around? girl, same energy, let's convert it:
use WebService::Dropbox::TokenFromOAuth1;
my $oauth2_access_token = WebService::Dropbox::TokenFromOAuth1->token_from_oauth1({
consumer_key => $dropbox->key,
consumer_secret => $dropbox->secret,
access_token => $access_token, # OAuth1 access_token
access_secret => $access_secret, # OAuth1 access_secret
});
warn $oauth2_access_token;
and then you just warn it out like the icon you are. π
π°οΈ Use API v1
π¨ heads up bestie: Dropbox will be turning off API v1 on 6/28/2017. That is a real deadline, not a vibe. If you're still on v1 you gotta deal with it.
π https://blogs.dropbox.com/developers/2016/06/api-v1-deprecated/
π cpanfile
requires 'WebService::Dropbox', '== 1.22';
π¦ cpanm
cpanm -L local ASKADNA/WebService-Dropbox-1.22.tar.gz
π curl
for the girlies who refuse to install anything, respect:
mkdir lib/WebService
curl -o lib/WebService/Dropbox.pm https://raw.githubusercontent.com/s-aska/p5-WebService-Dropbox/1.22/lib/WebService/Dropbox.pm
π οΈ API
π Auth
π https://www.dropbox.com/developers/documentation/http/documentation#oauth2-authorize
for CLI Sample π₯οΈ
my $url = $dropbox->authorize;
print "Please Access URL: $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";
for Web Sample π
use Amon2::Lite;
use WebService::Dropbox;
__PACKAGE__->load_plugins('Web::JSON');
my $key = $ENV{DROPBOX_APP_KEY};
my $secret = $ENV{DROPBOX_APP_SECRET};
my $dropbox = WebService::Dropbox->new({ key => $key, secret => $secret });
my $redirect_uri = 'http://localhost:5000/callback';
get '/' => sub {
my ($c) = @_;
my $url = $dropbox->authorize({ redirect_uri => $redirect_uri });
return $c->redirect($url);
};
get '/callback' => sub {
my ($c) = @_;
my $code = $c->req->param('code');
my $token = $dropbox->token($code, $redirect_uri);
my $account = $dropbox->get_current_account || { error => $dropbox->error };
return $c->render_json({ token => $token, account => $account });
};
__PACKAGE__->to_app();
authorize(\%optional_params)
super optional params, it's giving "do the bare minimum or go feral, your call":
# for Simple CLI
my $url = $dropbox->authorize();
# for Other
my $url = $dropbox->authorize({
response_type => 'code', # code or token
redirect_uri => '',
state => '',
require_role => '',
force_reapprove => JSON::false,
disable_signup => JSON::false,
});
π https://www.dropbox.com/developers/documentation/http/documentation#oauth2-authorize
token($code [, $redirect_uri])
This endpoint only applies to apps that are using the authorization code flow. Basically, an app calls this endpoint to grab a bearer token once the user has authorized the app. periodt.
Calls to /oauth2/token have to be authenticated using the app's key and secret. You can pass those either as POST parameters (see the params below) or through HTTP basic authentication. If you go the basic auth route, the app key is the username and the app secret is the password. iykyk.
# for CLI
my $token = $dropbox->token($code);
# for Web
my $token = $dropbox->token($code, $redirect_uri);
π https://www.dropbox.com/developers/documentation/http/documentation#oauth2-token
revoke
Disables the access token that was used to authenticate the call. it's the "we're so done here" button. π
my $result = $dropbox->revoke;
π https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke
π Files
copy($from_path, $to_path)
Copies a file or folder to a different location in the user's Dropbox. And if the source path happens to be a folder, all its contents come along for the ride too. π
my $result = $dropbox->copy($from_path, $to_path);
π https://www.dropbox.com/developers/documentation/http/documentation#files-copy
copy_reference_get($path)
Grabs a copy reference for a file or folder. That reference string can be used to save the file or folder into another user's Dropbox by passing it along to copy_reference/save. very giving-a-friend-a-key. π
my $result = $dropbox->copy_reference_get($path);
π https://www.dropbox.com/developers/documentation/http/documentation#files-copy_reference-get
copy_reference_save($copy_reference, $path)
Saves a copy reference that was returned by copy_reference/get into the user's Dropbox.
my $result = $dropbox->copy_reference_save($copy_reference, $path);
π https://www.dropbox.com/developers/documentation/http/documentation#files-copy_reference-save
create_folder($path)
Creates a folder at a given path. manifesting a directory fr. β¨
my $result = $dropbox->create_folder($path);
π https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder
delete($path)
Deletes the file or folder at a given path.
If the path is a folder, all its contents get deleted too. no survivors, sorry.
β οΈ A successful response indicates that the file or folder was deleted. The metadata that comes back will be the corresponding FileMetadata or FolderMetadata for the item at the time of deletion β it is not a DeletedMetadata object. don't get it twisted bestie.
my $result = $dropbox->delete($path);
π https://www.dropbox.com/developers/documentation/http/documentation#files-delete
delete_batch($paths)
Deletes the files or folders at a list of given paths. You hand over that list as the first arg in an arrayref.
A successful response means the files or folders got queued for async deletion. The object you get back will include the async_job_id. it's giving "we'll get to it". π«
my $result = $dropbox->delete_batch($paths);
π https://www.dropbox.com/developers/documentation/http/documentation#files-delete_batch
delete_batch_check($async_job_id)
Fetches the status of a asynchronous job for a delete_batch call.
It returns a status tag .tag with the possible values 'in_progress', 'complete' and 'failed'. If it succeeded, it also returns the list of results for each entry. π
my $result = $dropbox->delete_batch_check($async_job_id);
π https://www.dropbox.com/developers/documentation/http/documentation#files-delete_batch-check
download($path, $output [, \%opts])
Downloads a file from a user's Dropbox. the classic. π
# File handle
my $fh = IO::File->new('some file', '>');
$dropbox->download($path, $fh);
# Code reference
my $write_code = sub {
# compatible with LWP::UserAgent and Furl::HTTP
my $chunk = @_ == 4 ? @_[3] : $_[0];
print $chunk;
};
$dropbox->download($path, $write_code);
# Range
my $fh = IO::File->new('some file', '>');
$dropbox->download($path, $fh, { headers => ['Range' => 'bytes=5-6'] });
# If-None-Match / ETag
my $fh = IO::File->new('some file', '>');
$dropbox->download($path, $fh);
# $dropbox->res->code => 200
my $etag = $dropbox->res->header('ETag');
$dropbox->download($path, $fh, { headers => ['If-None-Match', $etag] });
# $dropbox->res->code => 304
π https://www.dropbox.com/developers/documentation/http/documentation#files-download
get_metadata($path [, \%optional_params])
Returns the metadata for a file or folder. the receipts, basically. π§Ύ
π Note: Metadata for the root folder is unsupported. the root is simply too mysterious, apparently.
my $result = $dropbox->get_metadata($path);
my $result = $dropbox->get_metadata($path, {
include_media_info => JSON::true,
include_deleted => JSON::true,
include_has_explicit_shared_members => JSON::false,
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata
get_preview($path, $outout [, \%opts])
Gets a preview for a file. Right now previews are only generated for the files with the following extensions: .doc, .docx, .docm, .ppt, .pps, .ppsx, .ppsm, .pptx, .pptm, .xls, .xlsx, .xlsm, .rtf. so if your file is not on this list, bestie, no preview for you. π
# File handle
my $fh = IO::File->new('some file', '>');
$dropbox->get_preview($path, $fh);
# Code reference
my $write_code = sub {
# compatible with LWP::UserAgent and Furl::HTTP
my $chunk = @_ == 4 ? @_[3] : $_[0];
print $chunk;
};
$dropbox->get_preview($path, $write_code);
# Range
my $fh = IO::File->new('some file', '>');
$dropbox->get_preview($path, $fh, { headers => ['Range' => 'bytes=5-6'] });
# If-None-Match / ETag
my $fh = IO::File->new('some file', '>');
$dropbox->get_preview($path, $fh);
# $dropbox->res->code => 200
my $etag = $dropbox->res->header('ETag');
$dropbox->get_preview($path, $fh, { headers => ['If-None-Match', $etag] });
# $dropbox->res->code => 304
π https://www.dropbox.com/developers/documentation/http/documentation#files-get_preview
get_temporary_link($path)
okay bestie we're literally mid-sentence here, hold that thought β part 2 is right there waiting and it has so much more for you. don't go anywhere. π¦
πΎ get_temporary_link($path)
okay so bestie, you want a temporary link to stream the content of a file? say less. This link expires in four hours and after that you will get 410 Gone. The Content-Type of the link is determined automatically by the file's mime type. no cap β¨
my $result = $dropbox->get_temporary_link($path);
my $content_type = $dropbox->res->header('Content-Type');
π https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link
πΌοΈ get_thumbnail($path, $output [, \%optional_params, $opts])
Get a thumbnail for an image. iconic behaviour honestly π
This method currently supports files with the following file extensions: jpg, jpeg, png, tiff, tif, gif and bmp. And lowkey important: photos that are larger than 20MB in size won't be converted to a thumbnail. the audacity but okay π
# File handle
my $fh = IO::File->new('some file', '>');
$dropbox->get_thumbnail($path, $fh);
my $optional_params = {
format => 'jpeg',
size => 'w64h64'
};
$dropbox->get_thumbnail($path, $fh, $optional_params);
# Code reference
my $write_code = sub {
# compatible with LWP::UserAgent and Furl::HTTP
my $chunk = @_ == 4 ? @_[3] : $_[0];
print $chunk;
};
$dropbox->get_thumbnail($path, $write_code);
# Range
my $fh = IO::File->new('some file', '>');
$dropbox->get_thumbnail($path, $fh, $optional_params, { headers => ['Range' => 'bytes=5-6'] });
# If-None-Match / ETag
my $fh = IO::File->new('some file', '>');
$dropbox->get_thumbnail($path, $fh);
# $dropbox->res->code => 200
my $etag = $dropbox->res->header('ETag');
$dropbox->get_thumbnail($path, $fh, $optional_params, { headers => ['If-None-Match', $etag] });
# $dropbox->res->code => 304
π https://www.dropbox.com/developers/documentation/http/documentation#files-get_thumbnail
π list_folder($path [, \%optional_params])
Returns the contents of a folder. your little peek inside the folder, we love to see it π
my $result = $dropbox->list_folder($path);
my $result = $dropbox->list_folder($path, {
recursive => JSON::false,
include_media_info => JSON::false,
include_deleted => JSON::false,
include_has_explicit_shared_members => JSON::false
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder
π list_folder_continue($cursor)
Once a cursor has been retrieved from list_folder, use this to paginate through all files and retrieve updates to the folder. so basically you got your cursor, now you keep scrolling π
my $result = $dropbox->list_folder_continue($cursor);
π https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue
πββοΈ list_folder_get_latest_cursor($path [, \%optional_params])
A way to quickly get a cursor for the folder's state. Unlike list_folder, list_folder/get_latest_cursor doesn't return any entries. This endpoint is for app which only needs to know about new files and modifications and doesn't need to know about files that already exist in Dropbox. it's giving "only the fresh tea, thank you" β
my $result = $dropbox->list_folder_get_latest_cursor($path);
my $result = $dropbox->list_folder_get_latest_cursor($path, {
recursive => JSON::false,
include_media_info => JSON::false,
include_deleted => JSON::false,
include_has_explicit_shared_members => JSON::false
});
β³ list_folder_longpoll($cursor [, \%optional_params])
A longpoll endpoint to wait for changes on an account. In conjunction with list_folder/continue, this call gives you a low-latency way to monitor an account for file changes. The connection will block until there are changes available or a timeout occurs. This endpoint is useful mostly for client-side apps. If you're looking for server-side notifications, check out our webhooks documentation. π
my $result = $dropbox->list_folder_longpoll($cursor);
my $result = $dropbox->list_folder_longpoll($cursor, {
timeout => 30
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-longpoll
π list_revisions($path [, \%optional_params])
Return revisions of a file. the whole glow-up history of your file, we stan a documented journey π
my $result = $dropbox->list_revisions($path);
my $result = $dropbox->list_revisions($path, {
limit => 10
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-list_revisions
π move($from_path, $to_path)
Return revisions of a file.
ok bestie we KNOW. the doc literally says "Return revisions of a file" here too β same sentence as list_revisions. not us, we're just the messenger π we copied it exactly, periodt.
my $result = $dropbox->move($from_path, $to_path);
π https://www.dropbox.com/developers/documentation/http/documentation#files-move
π permanently_delete($path)
Permanently delete the file or folder at a given path (see https://www.dropbox.com/en/help/40). as in GONE gone. no recycle bin, no undo, no takebacks. this is the dramatic one, be so careful π
Note: This endpoint is only available for Dropbox Business apps.
my $result = $dropbox->permanently_delete($path);
π https://www.dropbox.com/developers/documentation/http/documentation#files-permanently_delete
βͺ restore($path, $rev)
Restore a file to a specific revision. it's giving time travel, but make it filesystem π¦
my $result = $dropbox->restore($path, $rev);
π https://www.dropbox.com/developers/documentation/http/documentation#files-restore
π save_url($path, $url)
Save a specified URL into a file in user's Dropbox. If the given path already exists, the file will be renamed to avoid the conflict (e.g. myfile (1).txt). no overwrite drama here, the file just gets a little sequel π¬
my $result = $dropbox->save_url($path, $url);
π https://www.dropbox.com/developers/documentation/http/documentation#files-save_url
π΅οΈββοΈ save_url_check_job_status($async_job_id)
Check the status of a save_url job. because sometimes the vibe check is async π
my $result = $dropbox->save_url_check_job_status($async_job_id);
π https://www.dropbox.com/developers/documentation/http/documentation#files-save_url-check_job_status
π search($path, $query [, \%optional_params])
Searches for files and folders. detective era π΅οΈ β¨
Note: Recent changes may not immediately be reflected in search results due to a short delay in indexing.
my $result = $dropbox->search($path);
my $result = $dropbox->search($path, 'prime numbers', {
start => 0,
max_results => 100,
mode => 'filename'
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-search
β¬οΈ upload($path, $content [, \%optional_params])
Create a new file with the contents provided in the request.
Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start. for real, don't try it, it'll be a bad time π
# File Handle
my $content = IO::File->new('./my.cnf', '<');
my $result = $dropbox->upload($path, $content);
my $result = $dropbox->upload($path, $content, {
mode => 'add',
autorename => JSON::true,
mute => JSON::false
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-upload
π¦ upload_session($path, $content [, \%optional_params, $limit])
Uploads large files by upload_session API. big file energy, chunk by chunk π₯
# File Handle
my $content = IO::File->new('./mysql.dump', '<');
my $result = $dropbox->upload_session($path, $content);
my $result = $dropbox->upload_session($path, $content, {
mode => 'add',
autorename => JSON::true,
mute => JSON::false
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start π https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2 π https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish
π¬ upload_session_start($content [, \%optional_params])
Upload sessions allow you to upload a single file using multiple requests. This call starts a new upload session with the given data. You can then use upload_session/append_v2 to add more data and upload_session/finish to save all the data to a file in Dropbox.
A single request should not upload more than 150 MB of file contents. that's the ceiling bestie, respect it π«Ά
# File Handle
my $content = IO::File->new('./access.log', '<');
$dropbox->upload_session_start($content);
$dropbox->upload_session_start($content, {
close => JSON::true
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start
β upload_session_append_v2($content, $params)
Append more data to an upload session.
When the parameter close is set, this call will close the session.
A single request should not upload more than 150 MB of file contents. (same rule, different endpoint, she's consistent π)
# File Handle
my $content = IO::File->new('./access.log.1', '<');
my $result = $dropbox->upload_session_append_v2($content, {
cursor => {
session_id => $session_id,
offset => $offset
},
close => JSON::true
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2
π upload_session_finish($content, $params)
Finish an upload session and save the uploaded data to the given file path. the grand finale β¨
A single request should not upload more than 150 MB of file contents.
# File Handle
my $content = IO::File->new('./access.log.last', '<');
my $result = $dropbox->upload_session_finish($content, {
cursor => {
session_id => $session_id,
offset => $offset
},
commit => {
path => '/Homework/math/Matrices.txt',
mode => 'add',
autorename => JSON::true,
mute => JSON::false
}
});
π https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish
π₯ Users π
the people section!! account info incoming π«Ά
πββοΈ get_account($account_id)
Get information about a user's account.
my $result = $dropbox->get_account($account_id);
π https://www.dropbox.com/developers/documentation/http/documentation#users-get_account
π― get_account_batch(\@account_ids)
Get information about multiple user accounts. At most 300 accounts may be queried per request. 301 is too many bestie, the limit is the limit π€
my $result = $dropbox->get_account_batch($account_ids);
π https://www.dropbox.com/developers/documentation/http/documentation#users-get_account_batch
πͺ get_current_account()
Get information about the current user's account.
my $result = $dropbox->get_current_account;
π https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
π get_space_usage()
Get the space usage information for the current user's account. storage check szn, we've all been there π
my $result = $dropbox->get_space_usage;
π https://www.dropbox.com/developers/documentation/http/documentation#users-get_space_usage
π Sharing π
π create_shared_link_with_settings($path, $settings)
create_shared_link_with_settings($path, $settings)
okay so bestie, you wanna make a shared link with your own custom settings? Say less. β¨ If you don't pass any settings, the default visibility is RequestedVisibility.public β but lowkey the resolved visibility might depend on other stuff like team and shared folder settings, so don't come for me if it's not exactly that. π
my $result = $dropbox->create_shared_link_with_settings($path, $settings);
list_shared_links($path)
This one lists shared links of this user. No path? You get a list of all shared links for the current user. For members of business teams using team space and member folders, it returns all shared links in the team member's home folder unless the team space ID is specified in the request header. (For more info, peep the Namespace Guide, iykyk.) If you give a non-empty path, you get a list of all shared links that allow access to that path β direct links to it and links to its parent folders. You can suppress links to parent folders by setting direct_only to true. π¦
my $result = $dropbox->list_shared_links($path);
<https://www.dropbox.com/developers/documentation/http/documentation#sharing-list_shared_links>
modify_shared_link_settings($path, $settings, $remove_expiration)
Modify the shared link's settings, periodt. If the requested visibility conflicts with the shared links policy of the team or the shared folder (in case the linked file is part of a shared folder), then the LinkPermissions.resolved_visibility of the returned SharedLinkMetadata will reflect the actual visibility of the shared link, and LinkPermissions.requested_visibility will reflect the requested visibility. It's giving "what you asked for vs what you got", no cap. π
my $result = $dropbox->modify_shared_link_settings($path, $settings, $remove_expiration);
revoke_shared_link($url)
Revoke a shared link. But careful: even after you revoke a shared link to a file, the file may still be accessible if there are shared links leading to any of the file's parent folders. π To list all shared links that enable access to a specific file, you can use list_shared_links with the file as the ListSharedLinksArg.path argument. Slay. π
my $result = $dropbox->revoke_shared_link($url);
<https://www.dropbox.com/developers/documentation/http/documentation#sharing-revoke_shared_link>
Error Handling and Debug
So like, when things go sideways, here's how you peek under the hood. π΅οΈββοΈ
error: str
my $result = $dropbox->$some_api;
unless ($result) {
die $dropbox->error;
}
req: HTTP::Request or Furl::Request
my $result = $dropbox->$some_api;
warn $dropbox->req->as_string;
res: HTTP::Response or Furl::Response
my $result = $dropbox->$some_api;
warn $dropbox->res->code;
warn $dropbox->res->header('ETag');
warn $dropbox->res->header('Content-Type');
warn $dropbox->res->header('Content-Length');
warn $dropbox->res->header('X-Dropbox-Request-Id');
warn $dropbox->res->as_string;
env_proxyβ enableHTTP_PROXY,NO_PROXY
my $dropbox = WebService::Dropbox->new();
$dropbox->env_proxy;
debugβ enable or disable debug mode
my $dropbox = WebService::Dropbox->new();
$dropbox->debug; # disabled
$dropbox->debug(0); # disabled
$dropbox->debug(1); # enabled
verboseβ more warnings.
my $dropbox = WebService::Dropbox->new();
$dropbox->verbose; # disabled
$dropbox->verbose(0); # disabled
$dropbox->verbose(1); # enabled
AUTHOR
Shinichiro Aska
SEE ALSO
<https://www.dropbox.com/developers/documentation/http/documentation> <https://www.dropbox.com/developers/reference/migration-guide>
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
And that's a wrap! This page is from WebSer...Dropbox(3pm), perl v5.40.1, dated 2026-01-14. Thanks for reading, bestie! πβ¨π
π¦ where this page comes from
- ubuntu resolute: libwebservice-dropbox-perl 2.10-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libwebservice-dropbox-perl 2.10-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms