Added blogcl
authorTim Pope <code@tpope.net>
Mon, 9 Aug 2004 06:44:45 +0000 (06:44 +0000)
committerTim Pope <code@tpope.net>
Mon, 9 Aug 2004 06:44:45 +0000 (06:44 +0000)
perl/blogcl [new file with mode: 0755]

diff --git a/perl/blogcl b/perl/blogcl
new file mode 100755 (executable)
index 0000000..71403b8
--- /dev/null
@@ -0,0 +1,291 @@
+#!/usr/bin/perl
+# $Id$
+# -*- perl -*- vim: ft=perl sw=4 sts=4
+
+use strict;
+use Net::MovableType;
+use Getopt::Long;
+use File::Temp ();
+use Term::Complete;
+use vars qw(%opts $mt);
+
+if (-r $ENV{HOME} . "/.blogclrc") {
+    open CONFIG, $ENV{HOME} . "/.blogclrc";
+    while(<CONFIG>) {
+       s/\#.*//;
+       next unless m/^([^=]*)=(.*)/;
+       $opts{$1}=$2;
+    }
+    close CONFIG;
+}
+
+Getopt::Long::Configure ("bundling", "auto_help");
+die "Invalid arguments\n" unless
+GetOptions (\%opts, 'username|l=s', 'password|p=s', 'url|u=s', 'blogid|b=i', 'title|t=s', 'category|c=s', 'file|f=s');
+
+if ($ARGV[0] eq "help") {
+    Getopt::Long::HelpMessage();
+    exit(0);
+}
+
+sub init_mt {
+    die "No url given.\n" unless(defined("$url"));
+    die "No username given.\n" unless(defined("$username"));
+    die "No passowrd given.\n" unless(defined("$password"));
+    $mt = new Net::MovableType($opts{'url'}) || die "$!";
+    $mt->username($opts{'username'});
+    $mt->password($opts{'password'});
+    my $blogid = $opts{'blogid'} || $mt->resolveBlogId($opts{'username'});
+    unless($blogid) {
+       my $userblogs=$mt->getUsersBlogs();
+       if($userblogs && $userblogs->[0]->{blogid}) {
+           $blogid=$userblogs->[0]->{'blogid'};
+       } else {
+           warn "Blog ID unknown.\n";
+       }
+    }
+    $mt->blogId($blogid);
+}
+
+sub print_recent {
+    my $entries = $mt->getRecentPosts(shift || 10);
+    while ( my $entry = shift @$entries ) {
+       printf("[%02d] - %s\n",
+           $entry->{postid}, $entry->{title} );
+    }
+#   postid userid content title description dateCreated
+}
+
+sub print_categories {
+    print join("\n",map {$_->{categoryName}} @{$mt->getCategoryList()}), "\n";
+}
+
+sub show_post {
+    my $postid=shift;
+    my $post = $mt->getPost($postid) or die "$!";
+    my $categories = $mt->getPostCategories($postid) or die "$!";
+    if(defined($opts{file})) {
+       if($opts{file} eq "-") {
+           print STDOUT $post->{description}, "\n";
+       } else {
+           open FH, ">".$opts{file} or die "$!";
+           print $opts{file}, "\n";
+           print FH $post->{description}, "\n";
+           close FH;
+       }
+    } else {
+       printf("Title: %s (%d)\n", $post->{title}, $post->{postid});
+       if(@$categories>1) {
+           print "Categories:";
+       } elsif (@$categories==1) {
+           print "Category:";
+       }
+       foreach(@$categories) {
+           print " ", $_->{'categoryName'};
+       }
+       print "\n" if(@$categories);
+       print "Body:\n";
+       print $post->{description}, "\n";
+    }
+}
+
+sub edit_string {
+    my $string = shift;
+    if(defined($opts{file})) {
+       open FILE, $opts{file} or die "$!";
+       chop($string = join("", <FILE>));
+       close FILE;
+       return $string;
+    }
+    my $editor = $ENV{VISUAL} || $ENV{EDITOR};
+    $editor ||= "/usr/bin/sensible-editor" if (-x "/usr/bin/sensible-editor"); 
+    $editor ||= "/bin/vi" if (-x "/bin/vi"); 
+    $editor ||= "/usr/bin/nano" if (-x "/usr/bin/nano"); 
+    my $tmp = new File::Temp(SUFFIX => '.html');
+    print $tmp $string, "\n";
+    close $tmp;
+    my $mtime = (stat($tmp->filename))[9];
+    system(split (/ /, $editor), $tmp->filename);
+    if($mtime == (stat($tmp->filename))[9]) {
+       return undef;
+    } else {
+       open $tmp;
+       chop($string = join("", <$tmp>));
+       close $tmp;
+       return $string;
+    }
+}
+
+sub new_post {
+    my $title = $opts{title} || Complete('Title: ');
+    my $category = $opts{category} || Complete('Categories: ', map {$_->{categoryName}} @{$mt->getCategoryList()});
+    die "No title given.\n" unless($title);
+    my $description = edit_string("");
+    if(defined($description)) {
+       my $postid = $mt->newPost({ 'title' => $title, 'description' => $description });
+       $mt->setPostCategories($postid, $category ? [split(/[, ]+/, $category)] : $category) || warn "$!"
+       if(defined($category));
+       $mt->publishPost($postid);
+    } else {
+       die "No change. Aborting.\n";
+    }
+}
+
+sub edit_post {
+    my $postid=shift;
+    my $post = $mt->getPost($postid);
+    my $categories = $mt->getPostCategories($postid);
+    my $title = $opts{title} || Complete('Title: ', $post->{title}) || $post->{title};
+    my $description = edit_string($post->{description});
+    if(defined($description)) {
+       $mt->editPost($postid, { 'title' => $title, 'description' => $description });
+       $mt->setPostCategories($postid, $opts{category} ? [split(/[, ]/, $opts{category})] : $opts{category}) || warn "$!"
+       if(defined($opts{'category'}));
+       $mt->publishPost($postid);
+    } else {
+       die "No change. Aborting.\n";
+    }
+}
+
+
+if ($ARGV[0] eq "help") {
+    Getopt::Long::HelpMessage();
+} elsif ($ARGV[0] eq "categories") {
+    init_mt();
+    print_categories();
+} elsif ($ARGV[0] eq "list" && $ARGV[1] =~ /^\d*$/) {
+    init_mt();
+    print_recent($ARGV[1]);
+} elsif ($ARGV[0] eq "new") {
+    init_mt();
+    new_post();
+} elsif ($ARGV[0] eq "edit" && $ARGV[1] =~ /^\d+$/) {
+    init_mt();
+    edit_post($ARGV[1])
+} elsif ($ARGV[0] eq "show" && $ARGV[1] =~ /^\d+$/) {
+    init_mt();
+    show_post($ARGV[1])
+} else {
+    Getopt::Long::HelpMessage();
+    exit(1);
+}
+__END__
+=head1 NAME
+
+blogcl - Blog from the command-line
+
+=head1 SYNOPSIS
+
+B<blogcl> S<[ B<--title>|B<-t> "Title" ]> S<[ B<--category>|B<-c> category[,...] ]> S<[ B<--file>|B<-f> F<file> ]> S<[ B<--username>|B<-l> username ]> S<[ B<--password>|B<-p> password ]> S<[ B<--url>|B<-u> url ]> S<[ B<--blogid>|B<-b> blogid ]> { B<new> | B<edit> postid | B<show> postid | B<list> [count] | B<categories> }
+
+=head1 DESCRIPTION
+
+This is a simple script used to blog from the command-line.  It was designed
+for use with Drupal, but may work with any blog featuring a Movable Type
+compatible interface.
+
+One of the commands below must be present.
+
+=over 4
+
+=item B<new>
+
+Creates a new blog entry.
+
+=item B<edit>
+
+Edit an existing blog entry.  An existing postid must be specified.  To find a
+post's postid, use B<list>.
+
+=item B<show>
+
+Shows the contents of the post specified by the postid given on the
+command-line.
+
+=item B<list>
+
+List recents posts and their postids.  An optional integer specifies the number
+of posts to list.
+
+=item B<categories>
+
+Gives categories that are valid parameters to the B<--category> option.
+
+=item B<help>
+
+Show usage information.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-b>, B<--blogid>
+
+Specify a blog ID.  Defaults to the first blog found.
+
+=item B<-c>, B<--category>
+
+Specify a category for a new or existing entry.  For multiple categories,
+seperate them with a comma.  Ignored except when used with either B<new> or
+B<edit>.
+
+=item B<-f>, B<--file>
+
+When used with B<new> or B<edit>, specifies a file from which to read the
+post's body.  When used with B<show>, specifies a file to which to write the
+post's body.
+
+=item B<-l>, B<--username>
+
+Specify a username.
+
+=item B<-p>, B<--password>
+
+Specify a password.
+
+=item B<-t>, B<--title>
+
+Specify a title for a new or existing entry.  Ignored except when used with
+either B<new> or B<edit>.
+
+=item B<-u>, B<--url>
+
+Specify the URL of the XMLRPC interface.
+
+=back
+
+=head1 FILES
+
+=over 4
+
+=item F<~/.blogclrc>
+
+Contains options of format parameter=value.  Only the long form of
+options may be used.  Omit leading hyphens.
+
+=back
+
+=head1 SEE ALSO
+
+L<Net::MovableType>
+
+=head1 BUGS
+
+Error checking is minimal.
+
+=head1 COPYRIGHT
+
+Copyright by Tim Pope. All rights reserved.
+
+This library is a free software; you may redistribute it and/or modify it under
+the same terms as perl itself.
+
+=head1 AUTHORS
+
+Tim Pope, E<lt>perl@relongto.usE<gt>.
+
+L<http://www.sexygeek.org/>
+
+=cut