Scripting Access to America First Credit Union

Here’s a little script I thought might be useful for others out there. I run it each morning using a cron job. It logs in to my account at America First Credit Union and sends me a summary of recent account activity. (You’ll need to change some of the variables if you decide to use it.)

NOTE: storing your account number, PIN, etc. in plain text is a bad idea. Don’t do it. That being said, here’s the script where you might want to…


# Some modules to help things get started (available from
use Date::Calc qw(Today);
use Mail::Sender;
use WWW::Mechanize;
use Data::Dumper;

# User settings...
# Enter your account number here:
$user_id = '12345678';
# Enter your PIN here:
$user_pin = '1234';
# Replace with your security questions & answers: several questions could have been
# selected during the registration process. You'll need to edit the regular expressions
# and the corresponding answers so they match those from your account registration.
$secure_qa = {'favorite TV show' => 'Show Title',
'favorite movie' => 'Movie Title',
'favorite activity' => 'Activity Name'};
# One more piece of security...your passphrase which you supplied when registering.
# The browser will show this to you so you know you're contacting the correct site.
$pass_phrase = 'YOUR PHRASE HERE';
# Email configuration. Enter what works for your environment...
$smtp_server = '';
$smtp_user = 'username';
$smtp_pwd = 'password';
$email_from = '';
$email_to = '';
$email_cc_list = '',
# End of user settings

$email_summary = '';

# Setup mech object
$browser = new WWW::Mechanize;
$browser->agent_alias('Linux Mozilla');

# Load the AFCU login screen, enter the account # and click the button...
$browser->field("user_id", $user_id);
$browser->click_button(name => 'login_button');

# The login procedure bounces around a few pages before getting to the PIN...
$content = 'transactiontype=loginBean&USERID='.$user_id;
$res = $browser->content;

# If this is the first time running from the current environment, the system will
# ask to answer a security question.
if ($res =~ m/not recognized/i) {
$found_answer = 0;
for (keys %$secure_qa) {
if ($res =~ m/$_/i) {
$browser->field("answer", $secure_qa->{$_});
die $res."\nCannot register computer" unless $found_answer > 0;
$browser->field("doBind", "true");

# Check the pass phrase & enter the PIN
unless ($browser->content =~ m/$pass_phrase/i) {
die $browser->content."\n Incorrect security phrase.";
$browser->field("PARAM_PASSWORD", $user_pin);

# Now we're logged in, so let's snag some info from the screen for our email
$account_summary = parse_summary($browser->content);
$email_summary .= "BANK SUMMARY:\n\n".summarize($account_summary)."\n=============\n";

$email_summary .= "LAST 20 TRANSACTIONS:\n\n".parse_checking($browser->content);

# Now, let's send the email...
$sender = new Mail::Sender;
smtp => $smtp_server,
auth => 'LOGIN',
authid => $smtp_user,
authpwd => $smtp_pwd,
from => $email_from,
to => $email_to,
cc => $email_cc_list,
subject => 'Bank Summary',
msg => $email_summary,

sub parse_summary {
my $page = shift;
my $data = {};
$page =~ s/.+<\!-- Deposit processing -->//si;
$page =~ s/\<\/BLOCKQUOTE\>.+$//si;
$page =~ s/\*//g;
my @block = split(/<\!-- \w* processing -->/, $page);
foreach my $chunk (@block) {
my $type = "";
$chunk =~ s/.+<table[\w\s\"\=]*\>+//si;
$chunk =~ s/<\/table.*$//si;
$chunk =~ s/<tr>//sig;
$chunk =~ s/<td[\w\s\"\=]*\>//sig;
$chunk =~ s/\ \;//sig;
my @rows = split(/<\/tr>/, $chunk);
my $i = 0;
foreach my $row (@rows) {
my @rowdata = split(/<\/td>/, $row);
if ($i == 0) {
($type) = $rowdata[0] =~ m/\s?(\w*) Accounts/i;
} else {
my ($link) = $rowdata[0] =~ m/HREF=\"(.*)\"+/i;
my ($acc) = $rowdata[0] =~ m/\>(.*)<+/i;
$acc =~ s/<\/U>//i;
my ($bal) = $rowdata[1] =~ m/([\d\,]*\.+\d{2})/;
$bal =~ s/[^\d\.]//g;
$data->{$type}{$acc}{link} = $link;
$data->{$type}{$acc}{balance} = $bal;
return $data;

sub summarize {
my $data = shift;
my $summary = "";
foreach $type (sort(keys %$data)) {
$summary .= uc($type)." Accounts:\n";
$summary .= "-----------------\n";
foreach $acc (keys %{$data->{$type}}) {
next if $acc =~ m/^\s?$/;
$summary .= "$acc\t\t$data->{$type}{$acc}{balance}\n";
$data->{$type}{total} += $data->{$type}{$acc}{balance};
$summary .= "-----------------\n";
$summary .= "total:\t\t$data->{$type}{total}\n\n";
return $summary;

sub parse_checking {
my $page = shift;
$checking = "";
$page =~ s/.+<\!-- Register Information -->//si;
$page =~ s/.+<input type=hidden name=ChkImgURL value="\S*\"+\>+//si;
$page =~ s/\<\/BLOCKQUOTE\>.+$//s;
$page =~ s/\<\/table\>.+$//si;
$page =~ s/\ \;//ig;
$page =~ s/<tr>//ig;
$page =~ s/style=\"font-weight\: bold\;\"//ig;
$page =~ s/\s{2,}/ /ig;
$page =~ s/<td CLASS=\"\w*\"( nowrap)?\>//ig;
$page =~ s/\r\n|\r|\n//g;
my @rows = split(/<\/tr>/, $page);
my $i = 0;
foreach (@rows) {
my @values = split(/<\/td>/, $_);
next if !$values[0] && !$values[2] && !$values[3];
$values[0] =~ s/\s*//g;
$values[3] =~ s/\$//g;
push(@data, $record);
$checking .= "$values[0]\t$values[2]\t$values[3]\n";
last if $i >= 20;
return $checking;


Automatically downloading ACID 8-Packs

Here’s a little perl script I use to automatically snag those handy (& free) ACID loops each week. This will determine the name of the current 8Pack, download it, extract it, and delete the zip file–leaving you with a nice folder including the ACID project and the loops. Then, just run it each week with a cron job.


use LWP::UserAgent;
$downloaddir = '/home/username/8Packs/';

$url = '';
$ua = new LWP::UserAgent;
$req = new HTTP::Request(GET => $url);
$res = $ua->request($req);
$page = $res->content;

$page =~ m/href=\"http:\/\/dspcdn\.acidplanet\.com\/8packs\/([\w\d\.]*)\"/i;
$fn = $1;
die ("No filename to get") if !$fn;
$file_url = ''.$fn;

$fua = new LWP::UserAgent;
$freq = new HTTP::Request(GET => $file_url);
$fres = $fua->request($freq, $downloaddir.$fn);

if ($fres->is_success) {
$fn =~ m/(.*)\./;
my $extractto = $downloaddir.$1;
mkdir $extractto;
my $file = $downloaddir.$fn;
my $res = `unzip -d $extractto $file`;
unlink $file;
} else {
print $fres->status_line, "\n";


No More Waiting

Am I the only one who spends most of his life waiting? I remember waiting to be done with high school…then college. I also waited for just the right girl to come along so we could be married. I guess I’ve always thought I’d be done waiting someday…then things will start to happen. I’m realizing that waiting a big part of life.

I’ve figured out there are two kinds of waiting:

  1. Waiting for things that I’d like to speed up.
  2. Waiting for things I’d like to slow down.

Waiting for things I’d like to speed up

Right now I’m waiting to be out of debt. I made a plan while back. We’ve stuck to it pretty closely–adjusting it slightly along the way. Now it is just a waiting game. As long as I stick to my day job and keep spending under control, all the bills will be paid and our debt is reduced more and more every two weeks. It all happens automatically. So, I just wait for it to take care of itself and check in from time to time to make sure we’re on track.

I’m also waiting for one of my ideas to make it big. TheBigFork looks promising. Sam and I have always had ideas. We’ve gotten pretty good at executing them, too. Really the only problem is nobody ever notices. Now that the site is launched. I guess I’m waiting for it to be noticed.

Waiting for things I’d like to slow down

At the same time I’d like to speed up certain parts of life, I also want to slow it down. Lately it seems like weeks pass faster than the days did in high school. It worries me that I’m getting older and missing life as it passes by.

Fortunately, there are things like vacations to help slow it down. Next month, Sam and Dad and I are going on a backpacker campout. I’m not sure where we’re going, but it we’ll be gone two nights and parts of three days. I’m sure my Blackberry will be out of range, and books are too heavy for backpacking in to camp. So, what am I going to do with myself all day? I have no idea, but it will probably be a nice, long, no-stress day.

No More Waiting

My resolution for the day? Stop waiting. Take action to speed up certain things. Reserve time to slow things down. How to do it? I haven’t figured that part out yet. Maybe I’ll figure that out tomorrow.

“clear:both” My new best friend

This is probably not the most interesting post in the world, but I need to write it so I don’t forget it. I spent most of the day trying to get an HTML div tag to stretch its height to fit its contents. Sounds easy–right?

Well, it turns out, that if the div contains other elements which have the “float” property set, then the container div doesn’t stretch (in firefox and other browsers). I learned this is the correct behavior. Seems odd, but it is. IE behaves the way you would expect, but this is not compliant with W3C standards.

The solution? Add an element in the bottom of the container like this:

<div style="clear:both"></div>

I should’ve done some better Google searches earlier in the day. I found the answer pretty quickly once I put in the correct phrase. (firefox css float height)

Here’s the link I followed:

Cooking vs. Coding

Jason at 37Signals wrote about giving away your cookbook. This intrigued me for a number of reasons.

I noticed a correlation between geeks and high-quality food over a year ago. This post reinforces the notion that today’s computer geeks appreciate good cooking. (Otherwise, why would Jason and the responders even care to make the analogy?) Many will try their hand at it, too. I sure enjoy it. I have all but one of Bobby Flay’s books and love finding new ways to combine interesting flavors to create powerful new food experiences.

Programming has similarities to cooking. You start with basic building blocks and create something new for others to enjoy (or just for yourself). It is an art, and both seem to take the same kind of thinking to do it well.

In designing TheBigFork, Sam and I talked about Open Source Chefs. It is interesting that somebody else noticed the same thing. Someday we might expand the site with an Open Source Chef arena. For now, though, I think it is best to keep it simple–just like it says in 37Signal’s Getting Real. I hope my fellow geeks out there will enjoy TheBigFork and find it useful.

Fedora 7 – First Impression

Fedora 7 was officially released last week. I had a chance to install it on my main desktop machine at home over the weekend. I haven’t had a chance to dig in a lot, but so far it seems a lot like the last version.

The main difference I’ve seen so far is the presence of a lot of hot air balloons throughout the installer, rhgb, the default wallpaper, etc. I’m not sure why those are there. They’re kind of annoying and remind me of old Corel products.

The big plus this time around was the Xorg setup. The GUI tool actually worked in setting up dual displays on my ATI Radeon 9600. I’ve always been able to get this to work with previous versions, but it has always required lots of trial and error and manually editing the xorg.conf file. I probably will still tweak the file, but it was nice to have it work by itself.

Unfortunately, the same DVD which worked nicely on my main PC did not work at all on a spare IBM desktop of mine. It came up with a scrambled bootloader prompt the first time. After that, I couldn’t get it to do anything. I was hoping to make this my main machine since it is the newest, bestest machine in the house. I think it might not like the installed Matrox card. I’ll have to try it without that card to see if it behaves better.

Overall, I like Fedora 7. I don’t know if I’ve found anything worth upgrading my other machines yet, but still, it is tons better than Vista.