[wplug] pnc history

bpmedley at 4321.tv bpmedley at 4321.tv
Tue Sep 4 23:15:33 EDT 2001


Hi,

A while back I mentioned that I had made a perl program that would login to
pnc and get your account history.  Since then I have made several
improvments / changes.  This improved version is attached.  Below are some
of the differences:
    - added the concept of 'actions'
    - removed summary option (it's now an action)
    - modified output option
    - added in convience variables
    - fformat now uses convience variables

The 'actions' are predefined commands that the user gives to the script.
Specifically, it allows the user to change internal options on the fly and
instruct the script to do something with those options. This allows for a
rudimentary style of scripting.

For help on getting started please run the program with the -man option.

As before, this program requires some perl modules that are usually not
installed by default.  Even more unfortunate, is that I can't remeber the
exact modules that I needed to install to write it.  Sorry.  Here are a few
that I remeber:

LWP       - Library for WWW access in Perl
AppConfig - Module for reading configuration files and parsing command
            line arguments
Maybe Net::SSL for https urls.

If this is a problem for you, and you want to use this, please let me know
and I'll put some more time into figuring out what modules were required.

~'`^`'~=-.,__,.-=~'`^`'~=-.,__,.-=~'`^`'~=-., \|/  (___)  \|/ _,.-=~'`^`
                          Brian Medley         @~./'O o`\.~@
"Knowledge is Power" brian.medley at verizon.net /__( \___/ )__\  *PPPFFBT!*
  -- Francis Bacon                               `\__`U_/'
 _,.-=~'`^`'~=-.,__,.-=~'`^`'~=-.,__,.-=~'`^`'~= <____|'  ^^`'~=-.,__,.-=
~`'^`'~=-.,__,.-=~'`^`'~=-.,__,.-=~'`^`'~=-.,__,.-==--^'~=-.,__,.-=~'`^`
-------------- next part --------------
#! /usr/bin/perl -w

#
# $Header: /home/bmedley/docs/RCS/grab_pnc_history.pl,v 1.15 2001/09/05 03:01:04 bmedley Exp $ 
# 
# This program will goto PNC's web page and retrieve the user's account
# history.
# 
# Version:
#   $Revision: 1.15 $
#   $Date: 2001/09/05 03:01:04 $
#
# TODO
#   Fix error messages when using AppConfig.
#     - don't print out error messages when rc file not found, but do print out error
#     messages when supply wrong options

use strict;

use AppConfig;
use HTTP::Cookies;
use HTTP::Request::Common;
use HTML::Form;
use LWP::UserAgent;
use Pod::Usage;
use POSIX qw(strftime);
use Safe;

# use Data::Dump qw(dump); 
# use LWP::Debug qw(+);

my %cmdargs;
my $ua;

# 
# A hash describes the transactions for your accounts
#
# transactions{name}[x]{date}
# transactions{name}[x]{check_num}
# transactions{name}[x]{withdrawal}
# transactions{name}[x]{deposit}
# transactions{name}[x]{desc} <- array of lines
#
my %transactions;

#
# A hash of hashes describing your accounts
#
# accounts{name}{url_snippet}
# accounts{name}{number}
# accounts{name}{balance}
#
my %accounts;

#
# A hash containing option dependencies.
#
# When an option changes, it might affect other options.  For example, if the user
# changes the account, then the output filename should also change.
#
# dependencies{option}{array} <- an array of options to process when this one changes
#
my %dependencies;

# used when outputting a .qif file or when writing to a text file.
my $output_file;

sub error;
sub init;
sub manage_output;
sub verify_account;
sub verify_option;

my $HOME = $ENV{HOME};
our ($account);
our ($account_mod);
our ($end_date);
my $start_date = strftime ("%Y-%m-%d", localtime);

MAIN:
{
    init;

    # process the actions
    for (my $i = 0; $i < @{$cmdargs{action}}; $i++) {
        my $args = "";
        my $action = $cmdargs{action}[$i];

        if ($action =~ s/=(.*)//) {
            $args = $1;
            $args = "'" . $args . "'";
        }

        eval "&action_$action ($args)" or $@ =~ s/\n$//, die "Problem executing action:\n" .
             "\taction: $action\n" . 
             "\targs:   $args\n" .
             "\terror:  $@\n";

        # take care of dependencies
        if ($action =~ /change_option_(.*)/) {
            foreach (@{$dependencies{$1}}) {
                my $dep = "change_option_${_}=$cmdargs{$_}";
                splice @{$cmdargs{action}}, $i, 1, $cmdargs{action}[$i], $dep if 
                    $dep ne $cmdargs{action}[$i+1];
            }
        }
    } # end processing actions
    
    exit 0;
} # end MAIN:

# 
# Setup our environment
#
sub init
{
    my ($config, $filepth);
    my $cookie;

    $filepth = "$ENV{HOME}/.grab_pnc_historyrc";

    # don't print out error messages (internal to AppCongif):
    # (like config file not found)
    $config = AppConfig->new({ERROR => sub {;}, GLOBAL => {DEFAULT  => 0}});
    # 
    # print out error messages:
    # $config = AppConfig->new({GLOBAL => {DEFAULT  => 0}});

    $config->define ("debug",   {ARGS => "!"});
    $config->define ("date",    {ARGS => "=s"});
    $config->define ("userid",  {ARGS => "=s", DEFAULT => undef});
    $config->define ("passwd",  {ARGS => "=s", DEFAULT => undef});
    $config->define ("output",  {ARGS => "=s", DEFAULT => "stdout"});
    $config->define ("account", {ARGS => "=s", DEFAULT => "Interest Checking"});
    $config->define ("fformat", {ARGS => "=s", DEFAULT => '"${account_mod}_${start_date}.txt"'});
    $config->define ("action",  {ARGS => "=s@", DEFAULT => undef});
    $config->define ("help");
    $config->define ("man");

    # give command line options precedence over config file
    # the nobundling was required because -account wasn't working.  don't
    # know why...
    $config->file($filepth);
    $config->getopt(qw(nobundling), \@ARGV) or pod2usage(1);

    pod2usage(1) if $config->help;
    pod2usage(-verbose => 2) if $config->man;

    # i like the ease-of-use of AppConfig, but I would rather access the options like
    # variables, not subroutine calls.
    %cmdargs = ();
    %cmdargs = $config->varlist(".");

    # setup the aliases for our 'convience' variables
    *account  = \$cmdargs{account};
    *end_date = \$cmdargs{date};

    # don't know how to set these defaults via the DEFAULT config option on AppConfig
    if (-1 == $#{$cmdargs{action}}) {
        @{$cmdargs{action}} = ("login",
            'print_statement="$account Details\n\n"',
            "print_summary", 
            'print_statement="\n" . "=" x 80 . "\n\n"', 
            "print_history");
    }

    # this will, effectively, serve to validate our passed in options
    if (defined $cmdargs{userid}) {
        unshift @{$cmdargs{action}}, "change_option_userid=$cmdargs{userid}",
    }
    if (defined $cmdargs{passwd}) {
        unshift @{$cmdargs{action}}, "change_option_passwd=$cmdargs{passwd}",
    }
    unshift @{$cmdargs{action}}, 
        "change_option_account=$cmdargs{account}", # to intialize $account_mod
        "change_option_fformat=$cmdargs{fformat}",
        "change_option_output=$cmdargs{output}";

    # 
    # setup our option dependencies
    #
    $dependencies{debug}   = [];
    $dependencies{date}    = [];
    $dependencies{userid}  = [];
    $dependencies{passwd}  = [];
    $dependencies{output}  = [];
    $dependencies{account} = ["fformat"];
    $dependencies{fformat} = ["output"];
    $dependencies{action}  = [];

    # 
    # setup our "browser"
    #
    $ua = LWP::UserAgent->new;
    $ua->agent('Mozilla/4.73');

    # setup to save cookies only for this session (i.e. not save to disk)
    $ua->cookie_jar(HTTP::Cookies->new);
} # end init()

sub action_change_option_debug
{
    $cmdargs{debug} = shift;

    return 1;
} # end sub action_change_option_debug()

sub action_change_option_date
{
    $cmdargs{date} = shift;

    return 1;
} # end sub action_change_option_date()

sub action_change_option_userid
{
    my $userid = shift;

    die "The userid you supplied '$userid' is invalid\n" if $userid =~ /^\s*$/;
    
    # the javascript would have stripped out the dashes and spaces
    $userid =~ s/-//g;
    $userid =~ s/\s//g;

    $cmdargs{userid} = $userid;

    return 1;
} # end sub action_change_option_userid()

sub action_change_option_passwd
{
    my $passwd = shift;

    die "The userid you supplied '$passwd' is invalid\n" if $passwd =~ /^\s*$/;
    
    $cmdargs{passwd} = $passwd;

    return 1;
} # end sub action_change_option_passwd()

sub action_change_option_output
{
    $cmdargs{output} = shift;

    verify_option "output", $cmdargs{output}, ("stdout", "qif", "file");

    manage_output "check_switch_type";

    return 1;
} # end sub action_change_option_output()

sub action_change_option_account
{
    $cmdargs{account} = shift;

    $account_mod = $cmdargs{account};
    $account_mod =~ y/A-Z/a-z/;
    $account_mod =~ s/ /_/g;

    return 1;
} # end sub action_change_option_account()

sub action_change_option_fformat
{
    $cmdargs{fformat} = shift;

    # process the user specified format.
    eval '$output_file = ' . "$cmdargs{fformat}" or $@ =~ s/\n$//,
        die "Problem with fformat: '$cmdargs{fformat}' ($@)\n";

    manage_output "check_switch_name";

    return 1;
} # end sub action_change_option_fformat()

sub action_change_option_action
{
    my $action = shift;

    push @{$cmdargs{action}}, $action;

    return 1;
} # end sub action_change_option_action()

#
# This routine will determine if the user specified account was retrieved from pnc
#
sub verify_account
{
    if (not defined $accounts{$cmdargs{account}}) {
        die "Can't find account info for $cmdargs{account}.\n";
    }
} # end verify_account()

# 
# This routine makes sure that the value we are given is acceptable.
#
sub verify_option 
{
    my $option = shift;
    my $value = shift;
    my $is_ok;

    $is_ok = "no";

    # 
    # see if what the user gave us is allowed
    #
    foreach (@_) {
        if ($_ eq $value) {
            $is_ok = "yes";
            last;
        }
    }

    if ("yes" eq $is_ok) {
        return;
    }

    # else the user specified an invalid argument
    
    print STDERR "Value '$value' for the '$option' option is invalid.\n";
    print STDERR "We currently support:\n";
    foreach (@_) {
        print STDERR "\t$_\n" if "" ne $_;
        print STDERR "\t<empty string>\n" if "" eq $_;
    }

    exit 2;
} # end verify_option()
    
sub parse_accounts;
sub parse_history;

# 
# This subroutine goes to pnc's web page and dl's the user's history.
#
# It's highly sensitive to pnc's setup.  One reason for this is that pnc uses javascript
# in their forms, which is not easily used in perl.
#
sub action_login
{
    my ($request, $response);
    my ($content, $form, $url);
    my $i;

    #
    # login
    #
    if (not defined $cmdargs{userid}) {
        die "No userid given.\n";
    }
    if (not defined $cmdargs{passwd}) {
        die "No password given.\n";
    }
    if ($cmdargs{debug}) {
        print STDERR "Trying to login to pnc with userid: $cmdargs{userid}.\n";
    }
    
# =for TAKEOUT_TO_SPEED_DEVELOPMENT
    $request = HTTP::Request->new(GET => 
        'https://www.accountlink.pncbank.com/logon.jsp?HttpLevel=128');
    $response = $ua->request($request);

    # try and get rid of 'userid' is readonly warning
    $content = $response->content;
    $content =~ s#type="hidden" name="UserID"#type="text" name="UserID"#;

    $form = HTML::Form->parse( $content, $response->base());

    # give them the username/password
    $form->value( 'UserID',   "$cmdargs{userid}" );
    $form->value( 'Password', "$cmdargs{passwd}" );

    # the form does not have a submit button.  it uses javascript.  we have to add a submit
    # button.
    $form->push_input ( 'submit', {value=>"submit",name=>"submit"} );
    $response = $ua->request( $form->click('submit') );
# =cut   

    #
    # get the accounts
    #
    if ($cmdargs{debug}) {
        print STDERR "Retrieving information for your $cmdargs{account} account.\n";
    }

    # get rid of any accounts from the previous login
    %accounts = ();

# =for TAKEOUT_TO_SPEED_DEVELOPMENT
    $request = HTTP::Request->new(GET => 
        'https://www.accountlink.pncbank.com/alservlet/DepositAccountListServlet');
    $response = $ua->request ($request);

    parse_accounts $response->content_ref;
# =cut

# =for debug
#     open ACCOUNTS, "pnc_bank.accounts" or die "$!";
# 
#     local $/ = undef;
#     $response = <ACCOUNTS>;
#     parse_accounts \$response;
# 
#     close ACCOUNTS;
# =cut
    
    # 
    # get the history
    #
    if ($cmdargs{debug}) {
        print STDERR "Obtaining history from pnc.\n";
    }

    # get rid of any transactions from a previous login
    %transactions = ();

    if ($cmdargs{debug} && $cmdargs{date}) {
        print STDERR "Transactions before $cmdargs{date} will be ignored.\n";
    }

# =for TAKEOUT_TO_SPEED_DEVELOPMENT
    foreach (keys %accounts) {
        if ($cmdargs{debug}) {
            print STDERR "Fetching account history for $_.\n";
        }
        
        $url = 'https://www.accountlink.pncbank.com/Accounts/Deposit' .
            '/depositDetail.jsp?selectedPage=0&accountID=' .
            $accounts{$_}{url_snippet} . 
            '&More=INIT&Sort=DEFAULT&Page=0';
        $request = HTTP::Request->new(GET => $url);
        $response = $ua->request ($request);
        
        parse_history $response->content_ref, $_;
    }
# =cut

# =for DEBUG_CODE
#     foreach (keys %accounts) {
#         my $account = $_;
#         $account =~ y/A-Z/a-z/;
#         $account =~ s/ /_/g;
#         open HISTORY, "$account.history" or die "open ($account.history): $!";
# 
#         local $/ = undef;
#         $response = <HISTORY>;
#         parse_history \$response, $_;
# 
#         close HISTORY;
#     }
# =cut

    return 1;    
} # end action_login()

# 
# This routine takes the raw account information data from pnc (i.e. the HTML) and turns
# each entry into an element in the account hash
#
sub parse_accounts
{
    my $account_data = shift;
    my ($entry, $name);
    my @elements;

    while ($$account_data =~ /document.writeln\(buildRow\((.*)\)\);/mg) {
        $entry = $1;
        $entry =~ s/'//g;

        @elements = split /, /, $entry;

        $name = shift @elements;
        $accounts{$name} = {};

        ($accounts{$name}{url_snippet}, $accounts{$name}{number}, $accounts{$name}{balance}) = @elements;
    } # end parsing HTML from pnc

    if ($cmdargs{debug}) {
        print STDERR "We found the following accounts:\n";

        foreach my $key (sort { $accounts{$a} eq $accounts{$b} } keys %accounts) {
            print STDERR "$key\n";
        }

        print STDERR "\n";
    }

} #end parse_accounts ()

# 
# This routine takes the raw history data from pnc (i.e. the HTML) and turns each entry
# into an element in our transactions array.
#
sub parse_history
{
    my $history_data = shift;
    my $account = shift;
    my $element;
    my $entry;
    my $date;
    my $i;

    # step one in setting up our data structure
    $transactions{$account} = [];
    $element = $transactions{$account};
    
    while ($$history_data =~ /document.writeln\(buildRow\((.*)\)\);/mg) {
        $entry = $1;
        $entry =~ s/'//g;

        # convert dates into iso 8601 date format
        $entry =~ s/(.*?),\s*(.*)/$2/;
        $date  = $1;
        $date  =~ m#(\d\d)/(\d\d)/(\d\d\d\d)#;
        $date  = "$3-$1-$2";

        # only give the user the date range they want
        if ($cmdargs{date}) {
            return if $date lt $cmdargs{date};
        }

        # initialize our data structure
        $i = scalar @$element;
        @$element[$i] = {};
        @$element[$i]->{desc} = [];

        # store the values in our data structure
        @$element[$i]->{date} = $date;
        (@$element[$i]->{check_num}, @$element[$i]->{withdrawal}, @$element[$i]->{deposit},
        @$element[$i]->{desc}[0], @$element[$i]->{desc}[1]) = split /, /, $entry;

        # if there was only one line in the description
        $#{@$element[$i]->{desc}}-- if @$element[$i]->{desc}[1]=~ /\&\#160/;

        # tidy up check output
        @$element[$i]->{desc}[0] =~ s/^CHECK\s+(\d+)\s+(\S+)/CHECK $1 $2/;
    } # end parsing the html from pnc

} # end parse_history()

# 
# This is a wrapper action routine that will call the real 'print_summary' routine.  
#
sub action_print_summary
{
    verify_account;

    manage_output "begin_write";
    
    eval "&print_summary_$cmdargs{output}" or $@ =~ s/\n$//, 
        die "Can't print summary using: print_summary_$cmdargs{output} ($@)\n";
}

#
# This routine will print the user's summary to stdout.
#
sub print_summary_stdout
{
    my $acct;
    
    $acct = ($cmdargs{account});

    printf "%-30s%-29s%-s\n", $acct, $accounts{$acct}{number}, $accounts{$acct}{balance};
    
    return 1;
} # end print_summary_stdout()

# 
# This routine will print the user's summary to a file
#
# Remember, that when we print to a file or stdout we just select the default filehandle
# appropriately.
#
sub print_summary_file
{
    print_summary_stdout;
} # end print_summary_file()

#
# This subroutine shows that we don't print any summary if the user wants qif
# file output.
#
sub print_summary_qif
{
    die "Unable to print_summary when writing .qif files.\n";
    
    return 1;
}

# 
# This is a wrapper action routine that will call the real 'print_history' routine.  
#
sub action_print_history
{
    verify_account;

    manage_output "begin_write";
    
    eval "&print_history_$cmdargs{output}" or $@ =~ s/\n$//, 
        die "Can't print history using: print_history_$cmdargs{output} ($@)\n";
}

# 
# This routine prints out the transactions array to stdout (More accurately, it prints out
# to the default filehandle.  But, this name makes it more straightforward in actions).
#
sub print_history_stdout
{
    my $line;
    my $tranref = $transactions{$cmdargs{account}};

    if (0 == @$tranref) {
        print "No transactions recorded.\n";
        return 1;
    }
    
    foreach (@$tranref) {
        # we don't print check_num b/c it's in the description

        printf "%s   %-46s%-15s%-s\n", $_->{date}, $_->{desc}[0], $_->{withdrawal}, $_->{deposit};
        
        # if the description is multi-line this takes care of it
        foreach $line (1 .. $#{$_->{desc}}) {
            print "             $_->{desc}[$line]\n";
        }
        
        # this is so the user can add their own description.  the spaces are so they can
        # hit 'A' in vi and be lined up with the others.
        print     "             \n";
    }

    return 1;
} # end print_history_stdout()

# 
# This routine prints out the transactions array to a file
#
# Remember, that when we print to a file or stdout we just select the default filehandle
# appropriately.
#
sub print_history_file
{
    print_history_stdout;
} # end print_history_file()

# 
# This routine generates a qif file from our transactions array.  It's not tested a
# great deal, because I don't use qif files.
# 
# example for qif files:
# http://www.intuit.com/quicken/technical-support/quicken/old-faqs/dosfaqs/60006.html
# 
sub print_history_qif
{
    my $amt;
  
    foreach (@{$transactions{$cmdargs{account}}}) {
        print "!Type:Bank\n";
        print "D$_->{date}\n";
        
        $_->{check_num} =~ s/\s*//g;
        if ($_->{check_num} =~ /^\d+$/) {
            print "N$_->{check_num}\n";
        }
        
        $amt = $_->{withdrawal};
        
        if ($amt =~ /^\$/) {
            # we have a withdrawal
            $amt =~ s/\$/-/;
        } else {
            # we have a deposit
            $amt = $_->{deposit};
            $amt =~ s/\$//;
        }

        print "T$amt\n";
        print "P$_->{desc}[0]\n";
        print "M$_->{desc}[1]\n" if defined $_->{desc}[1];

        print "^\n";
    }

    return 1;
} # end print_history_qif()

#
# This routine will make the default filehandle point wherever the user wants output to
# go.
#
# It can be called in the following cases:
#
# - The file name changes (via fformat change) [check_switch_name]
#   > store change if needed
#
#   o we are writing to stdout
#     > file modifications unnecessary
#     
#   o we are writing to a file (text or qif)
#     x we have not started writing yet ($output_file undefined)
#       > file modifications unnecessary
#     x we may have written something   ($output_file defined)
#       > possibly close output_file (if file name changed)
#
# - The output destination changes [check_switch_type]
#   o we are going to write to stdout
#     > possibly close output_file
#     
#   o we are going to write to a file (file or qif)
#     > file modifications unnecessary
#   
# - Going to write something [begin_write]
#   o we are going to write to stdout
#     > select stdout
#     
#   o we are going to write to a file (file or qif)
#     > possibly open output_file
#     > select output_file
#     
BEGIN {
my $stored_output_file;
my $output_file_opened = "no";

sub manage_output
{
    my $action = shift;
    my $changed;
    my $new;

    if ("check_switch_name" eq $action) {
        # determine if the file name has changed
        #   - if the stored is different from the global 
        #                   or
        #   - if there is no stored, but there is a new
        $changed = (defined $stored_output_file && 
                  defined $output_file && 
                  ($stored_output_file ne $output_file)) || (!defined $stored_output_file && defined $output_file);
        if ($changed) {
            $stored_output_file = $output_file;
        }
        
        if ("stdout" eq $cmdargs{output}) {
            ; # nothing necessary
        }

        if ("file" eq $cmdargs{output} || "qif" eq $cmdargs{output}) {
            if (not defined $output_file) {
                ; # nothing necessary
            } elsif ($changed) {
                if ("yes" eq $output_file_opened) {
                    close OUTPUT_FILE;
                    $output_file_opened = "no";
                }
            } # end and the file changed
        } # end if we are writing to a file

        return;
    }

    if ("check_switch_type" eq $action) {
        if ("stdout" eq $cmdargs{output}) {
            if ("yes" eq $output_file_opened) {
                close OUTPUT_FILE;
                $output_file_opened = "no";
            }
        }

        if ("file" eq $cmdargs{output} || "qif" eq $cmdargs{output}) {
            ; # nothing necessary
        }

        return;
    }
            
    if ("begin_write" eq $action) {
        if ("stdout" eq $cmdargs{output}) {
            select STDOUT;
        }

        if ("file" eq $cmdargs{output} || "qif" eq $cmdargs{output}) {
            if ("no" eq $output_file_opened) {
                open OUTPUT_FILE, "$output_file" or die "open ($output_file): $!\n";
                $output_file_opened = "yes";

                if ($cmdargs{debug}) {
                    print STDERR "Filename to write to is: $output_file\n";
                }
            }
            select OUTPUT_FILE;
        }

        return;
    }

}} # end manage_output()

# 
# This routine will allow the user to 'print' something to the screen.
#
sub action_print_statement
{
    my $statement = shift;

    die "Unable to print_statement when writing .qif files.\n" if "qif" eq $cmdargs{output};
    
    manage_output "begin_write";
    
    eval "print $statement" or $@ =~ s/\n$//s, die "Can't print '$statement' ($@)\n";

    return 1;
}

__END__

=pod

=head1 NAME

grab_pnc_history.pl - Goto PNC's web page and retrieve the user's account data

=head1 SYNOPSIS

B<grab_pnc_history.pl> [options]

=head1 OPTIONS

=over 4

=item B<-help>

Prints some help and exits.

=item B<-man>

Prints the manual page and exits.

=item B<-date>

Used to specify a starting date when producing output.  No transaction before
this date will be processed.  

date should be specified in the ISO 8601 format (e.g. 2001-08-04).

=item B<-debug>

Prints some (hopefully) helpful messages during execution to figure out
what's going on.

=item B<-userid>

The user id you login to pnc with.

=item B<-passwd>

The password for that user id.

=item B<-account>

The account you want to get information for.  The default is "Interest Checking".
Known values are:

    Interest Checking
    Regular Checking
    Statement Savings

=item B<-output>

What format you want your output in.  

This option take the following arguments:

    stdout - print data to stdout 
    qif    - save output to a qif file
    file   - save output to a text file

The default is stdout.  If you select qif output, then summaries can not be printed.

=item B<-fformat>

This option controls how filenames are generated.  It is used when the "file" or "qif"
output option is present.  It will be parsed like a perl statement, so variables can
be used in it's defintion.  There are several "convience" variables available for use with 
this option.  These are described in the "CONVIENCE VARIABLES" section.

The default is: "${account_mod}_${start_date}.txt" (note the quotes.  they are required)

NOTE: Technically, this option will be the EXPR used by 'open'.

=item B<-action>

This option allows the user to control how this script operates.  Conceptually, this will
allow the user to say "login and print the account summaries to the screen, but print the
histories to a file".  However, it does not use such free-form text to accomplish this.  
Please see the section "ACTIONS" below for a much more detailed description.

=back

=head1 DESCRIPTION

This program will goto PNC's web page and retrieve the user's account data.

=head1 CONFIGURATION

This program accepts information from the command line and from a
configuration file.  The command line overrides values placed inside the
configuration file.  At present the configuration file is:

    ~/.grab_pnc_historyrc

An example is: 

    # my account
    userid  = 478661344
    passwd  = 1234
    account = Interest Checking   

More generally, this is:
    
    option [=] [value]

Where "option" is a command line option.  This allows the user to specify
their own defaults and makes sure that the password does not show up in ps(1)
and the like.  

NOTE: Passwords are stored in plaintext.

=head1 EXAMPLES

Use the values in the configuration file and get "Statement Savings" history.

    grab_pnc_history.pl -account="Statement Savings"

Use the values in the configuration file and get "Statement Savings" history.
Send the output to the screen.  However, only transactions that happened 
AFTER and INCLUDING Wed, August 15th, 2001 will be printed.

    grab_pnc_history.pl -account="Statement Savings" -output=stdout -date=2001-08-15

Use the values in the configuration file and get "Regular Checking" history.
Send the output to a qif file (note the quotes with fformat).

    grab_pnc_history.pl -account="Regular Checking" -fformat='"$account.qif"' -output=qif

=head1 NOTES

This program needs the following perl modules (which, to the best of my 
knowledge are not installed by default):

    LWP       - Library for WWW access in Perl
    AppConfig - Module for reading configuration files and parsing command line arguments
    (there are probably others, but I've forgotten what I had to install)

I don't know the minimum perl version that is usable.  I use 5.6.0.

This program was adopted from code written by Ronald Hill.  His program will dl
account history from Wells Fargo.

=head1 CONVIENCE VARIABLES

The fformat option and print_statement action both accept a perl expression which 
will be evaluated to determine their value.  This means you can insert variables
into the expression.  As a convience to the user, there are several variables 
available to them.  They are listed below.

=for text
$account     = Name of account. Listed verbatim. (e.g. "Interest Checking")
$account_mod = Name of account. Spaces coverted to underscores and all lowercase. 
               (e.g. "interest_checking")
$begin_date  = The date that the script started.
$end_date    = The -date option.


So, if you wanted to place the output in files based off of the account names, you 
set the fformat option to something like (note the quotes):
    "$account_mod.txt"

=head1 ACTIONS

Actions are predefined commands that the user gives to the script.  Specifically, it
allows the user to change internal options on the fly and instruct the script to do
something with those options.  This allows for a rudimentary style of scripting.  It's
goal is to provide the user with a fair amount of flexibilty when producing output and the
like.  

These actions can be specified on the command-line, or in the configuration file.  To do
something useful it generally requires many actions, so the suggested practice is to
place them in the configuration file.  The full action listing is below:

    change_option_account
    change_option_action
    change_option_debug
    change_option_fformat
    change_option_output
    change_option_date
    change_option_passwd
    change_option_userid
    login
    print_summary
    print_history
    print_statement

The first several allow for the script options to be changed during run-time.  There is a
caveat with the "date", "passwd", and "userid" variables.  They will not take affect until
the next "login".

After these, comes actions that interact with bank data.  Below is a description:

    login         => goto pnc and get the user's account information
    print_summary => print the summary for the currently selected account
    print_history => print the history for the currently selected account

Last is a utility action.  It gives the user the ability to print an arbitrary perl
expression.  Here are the default actions (they are listed as if in a config file):

    action = login
    action = print_statement="$account Details\n\n"
    action = print_summary
    action = print_statement="\n" . "=" x 80 . "\n\n"
    action = print_history

=cut


More information about the wplug mailing list