App-Tweet

 view release on metacpan or  search on metacpan

lib/App/Tweet.pm  view on Meta::CPAN


use warnings;
use strict;

use Config::YAML;
use Crypt::CBC;
use File::HomeDir;
use File::Slurp;
use File::Spec;
use File::Touch;
use IO::Interactive qw(is_interactive);
use Log::Log4perl qw(:easy);
use Net::Twitter;
use String::Random;
use Term::Prompt;

Log::Log4perl->easy_init($ERROR);

use constant TWEET_CONFIG_FILE => '.tweet';
use constant TWEET_CIPHER_FILE => '.teewt';

our $VERSION = '1.02';

sub run {
    my ( $class, %args ) = @_;

    DEBUG "$_ => [$args{$_}]" for keys %args;

    die ERROR "message is a required argument" unless exists $args{message};

    _send_message( $args{message}, _get_configuration( \%args ) );
}

sub reconfigure {
    unlink File::Spec->join( File::HomeDir->my_data(), TWEET_CONFIG_FILE );
    _get_configuration();
}

sub _get_configuration {
    my ($args) = @_;
    my $conf = _read_configuration_file(
        File::Spec->join( File::HomeDir->my_data(), TWEET_CONFIG_FILE ),
        $args );
    return $conf;
}

sub _read_configuration_file {
    my ( $config_file, $args ) = @_;

    DEBUG "trying to read config file [$config_file]";

    my $cipher_key = _get_cipher_key();

    DEBUG "using cipher [$cipher_key]";

    my $cipher = Crypt::CBC->new( -key => $cipher_key, -cipher => 'Blowfish' );

    if ( not -e $config_file ) {
        DEBUG "creating config file [$config_file]";
        touch $config_file if not -e $config_file;
        chmod oct(600), $config_file;
    }

    my $config = Config::YAML->new( config => $config_file, );

    $config->{username} = $args->{username} if exists $args->{username};
    $config->{password} = $cipher->encrypt( $args->{password} )
      if exists $args->{password};

    if ( not defined $config->{username} ) {

        DEBUG "unable to find user name in configuration file";

        die ERROR
          "can't prompt for config file values in non-interactive environment"
          unless is_interactive();

        $config->{username} =
          prompt( 'x', 'Username: ', 'from twitter.com', qw{} );
        $config->write();
    }
    if ( not defined $config->{password} ) {

        DEBUG "unable to find password in configuration file";
        $config->{password} = $cipher->encrypt(
            prompt( 'p', 'Password: ', 'from twitter.com', qw{} ) );

        $config->write();
    }

    DEBUG "password [$config->{password}]";

    $config->{password} = $cipher->decrypt( $config->{password} );

    DEBUG "password [$config->{password}]";

    return $config;
}

sub _get_cipher_key {
    my $cipher_file =
      File::Spec->join( File::HomeDir->my_data(), TWEET_CIPHER_FILE );

    if ( not -e $cipher_file ) {

        DEBUG "cipher file not found, creating it [$cipher_file]";

        my $cipher_key = String::Random->new()->randpattern( '.' x 56 );

        DEBUG "created new cipher key [$cipher_key]";

        write_file( $cipher_file, $cipher_key );
        chmod oct(600), $cipher_file;

        return $cipher_key;
    }

    DEBUG "reading cipher file [$cipher_file]";

    return read_file($cipher_file);
}

sub _send_message {
    my ( $message, $config ) = @_;

    DEBUG "accessing twitter as [$config->{username}]";

    my $twitter = Net::Twitter->new(
        username => $config->{username},
        password => $config->{password},
    );

    DEBUG "sending message to twitter [$message]";

    $twitter->update($message)
      or ERROR
"something bad happened and I couldn't sent your message.  you might not be able to connect to twitter (try visiting twitter.com in your browser) or you might be using the wrong user name or password.";

    return;
}

1;

__END__

=pod

=head1 NAME

App::Tweet - tweet on twitter from the command line 

=head1 SYNOPSIS

  use App::Tweet;
  
  App::Tweet->run('tell this to twitter');
  
  App::Tweet->reconfigure;

=head1 DESCRIPTION

C<App::Tweet> is a simple wrapper around L<Net::Twitter> that allows for you to easily send
messages (tweets) to twitter.com as a specific user.  You should use the 'tweet' command to
interface with this module.

The first time you C<run> the application it will prompt you for a user name and password.  This 
information is stored in a configuration file in your system's application data store.  The 
password is stored in a somewhat encrypted state, but the cipher key for the encryption is
stored right beside the configuration file, so it's not super-security.  The permissions on 
the file are set to read/write only by the file owner, but that is only relevant on some systems.

If you ever need to reset or change the username or password perminantely, you can use the
C<reconfigure> method.  If the change is just temporary, pass in the new username and password



( run in 0.905 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )