changeset 5:dddb84241032

Bot can now read skills from the API for its friends
author Dominic Cleal <dominic@computerkb.co.uk>
date Sat, 06 Dec 2008 14:48:58 +0000
parents 5cba712b27aa
children f0f55a4999b7
files skillbot.pl
diffstat 1 files changed, 54 insertions(+), 250 deletions(-) [+]
line wrap: on
line diff
--- a/skillbot.pl	Sat Dec 06 14:40:54 2008 +0000
+++ b/skillbot.pl	Sat Dec 06 14:48:58 2008 +0000
@@ -4,22 +4,23 @@
 
 =pod
 
-twitfolk
+skillbot
 
-Gate tweets from your Twitter friends into an IRC channel.  Currently can be
-found on irc://irc.bitfolk.com/bitfolk as the user "Twitfolk".
+Provides notifications of EVE Online skill completions over IRC.
+
+Based on Andy Smith's twitfolk bot:
+$Id: twitfolk.pl 802 2008-11-29 00:07:38Z andy $
 
 Copyright ©2008 Andy Smith <andy+twitfolk.pl@bitfolk.com>
+Portions copyright ©2008 Dominic Cleal <dominic@computerkb.co.uk>
 
 Artistic license same as Perl.
-
-$Id: twitfolk.pl 802 2008-11-29 00:07:38Z andy $
 =cut
 
 use strict;
 use warnings;
 
-use Net::Twitter;
+use WebService::EveOnline;
 use Data::Dumper;
 use Net::IRC;
 use HTML::Entities;
@@ -33,9 +34,7 @@
 
 my %friends;
 
-my $last_tweet = 0;
-
-open(CONFIG, "< twitfolk.conf") or die "can't open twitfolk.conf for reading: $!";
+open(CONFIG, "< skillbot.conf") or die "can't open skillbot.conf for reading: $!";
 while(<CONFIG>) {
 	chomp;
 	s/#.*//;
@@ -45,10 +44,10 @@
 	my ($var, $value) = split(/\s*=\s*/, $_, 2);
 	$config{$var} = $value;
 }
-close(CONFIG) or die "can't close twitfolk.conf: $!";
+close(CONFIG) or die "can't close skillbot.conf: $!";
 
-my $version = '0.00001';
-my $ircname = "twitfolk v$version";
+my $version = '0.01';
+my $ircname = "skillbot v$version";
 
 my $DEBUG = $ENV{'IRC_DEBUG'} || 0;
 my $time_to_die = 0;
@@ -60,19 +59,8 @@
 print PIDFILE "$$\n";
 close(PIDFILE) or die "can't close $config{'pidfile'}: $!";
 
-my $twit = Net::Twitter->new(username => $config{twitter_user},
-							 password => $config{twitter_pass});
-
-die "Can't connect to Twitter: $!" unless $twit;
-
-#$twit->update(sprintf("Connecting to irc://%s/", $config{target_server}));
-#$twit->http_code == 200 or die "Twitter->update: $twit->http_message()";
-
-sync_friends(undef);
 update_friends(undef);
 
-$last_tweet = init_last_tweet();
-
 my $irc = new Net::IRC;
 
 my $conn = $irc->newconn(Server => $config{'target_server'},
@@ -144,16 +132,11 @@
 	add_repeat_timer(600, sub { my ($timer, $self) = @_; join_channels($self); });
 
 	# Read the "friends" config file every 6 minutes and make sure we have
-	# friended them all
+	# API sessions for them all
 	add_repeat_timer(360, sub { my ($timer, $self) = @_; update_friends($self); });
 
-	# Ask Twitter who our friends are every hour and make sure they are
-	# known to us
-	add_repeat_timer(3600, sub { my ($timer, $self) = @_; sync_friends($self); });
-
-	# Check for new tweets every 5 minutes.  API allows 100 calls every 60
-	# minutes so should be okay
-	add_repeat_timer(300, sub { my ($timer, $self) = @_; check_tweets($self); });
+	# Check for new skills every 5 minutes.
+	add_repeat_timer(300, sub { my ($timer, $self) = @_; check_training($self); });
 }
 
 sub nickserv_id_now
@@ -202,7 +185,7 @@
 =cut
 
 		# Now we're in, check for tweets as a one-off
-		add_one_shot_timer(10, sub { my ($timer, $self) = @_; check_tweets($self); });
+		add_one_shot_timer(10, sub { my ($timer, $self) = @_; check_training($self); });
     }
 }
 
@@ -347,9 +330,9 @@
 Read a list of friends from the friends_file.  These will be friended in
 Twitter if they aren't already.  Format is:
 
-screen_name		IRC_nick
+character_name	user_id	api_key		IRC_nick
 
-Start a line with # for a comment.  Any kind of white space is okay.
+Start a line with # for a comment.  Columns must be tab separated.
 
 =cut
 sub update_friends
@@ -361,34 +344,35 @@
 	while (<FF>) {
 		next if (/^#/);
 
-		if (/^(\S+)\s+(\S+)/) {
-			my $f = lc($1);
-			my $nick = $2;
+		if (/^([^\t]+)\t+([0-9]+)\t+([A-F0-9]{64})\t+([^\t]+)/i) {
+			my $c = $1;
+			my $uid = $2;
+			my $key = uc($3);
+			my $nick = $4;
+
+			if (! $friends{$c}) {
+				my $api = WebService::EveOnline->new( { user_id => $uid,
+														api_key => $key } );
 
-			if (! $friends{$f}) {
-				my $u = $twit->show_user($f);
-
-				if ($twit->http_code != 200) {
-					irc_debug("twitter->show_user(%s) failed: %s", $f,
-						$twit->http_message);
+				foreach my $character ($api->characters) {
+					next if $c ne $character->name;
+					$friends{$c}->{char} = $character;
+					last;
+				}
+				
+				unless (defined $friends{$c}->{char})
+				{
+					irc_debug("EVE: Unable to find character %s for ID %lu",
+							  $c, $uid);
 					next;
 				}
-
-				my $id = $u->{id};
-				$friends{$f}->{id} = $id;
-
-				irc_debug("Twitter: Adding new friend '%s' (%lu)", $f,
-					$id);
+						
+				$friends{$c}->{api} = $api;
 
-				$twit->create_friend($id);
-
-				if ($twit->http_code != 200) {
-					irc_debug("twitter-> create_friend($id) failed: %s",
-						$twit->http_message);
-				}
+				irc_debug("EVE: Adding new friend '%s' =~ %s", $c, $nick);
 			}
 
-			$friends{$f}->{nick} = $nick;
+			$friends{$c}->{nick} = $nick;
 		}
 	}
 
@@ -397,216 +381,36 @@
 
 =pod
 
-Learn friends from those already added in Twitter, just in case they got added
-from outside as well.  Might make this update the friends file at some point.
+Check for any characters that aren't known to be training, then call the API
+to see if they've started, setting timers.
 
 =cut
-sub sync_friends
+sub check_training
 {
 	my $self = shift;
 
-	my $twitter_friends = $twit->friends({
-			id => $config{twitter_id}
-		});
-
-	if ($twit->http_code != 200) {
-		irc_debug("twitter->friends() failed: %s", $twit->http_message);
-		return;
-	}
-
-	foreach my $f (@{ $twitter_friends }) {
-		my $screen_name = lc($f->{screen_name});
-		my $id = $f->{id};
-
-		$friends{$screen_name}->{id} = $id;
-
-		if (! defined $friends{$screen_name}->{nick}) {
-			$friends{$screen_name}->{nick} = $screen_name;
-		}
-
-		irc_debug("Twitter: Already following '%s' (%lu)", $screen_name,
-			$friends{$screen_name}->{id});
-	}
-
-}
-
-=pod
-
-Get a friends timeline and announce it to IRC.  Only does $max at once and only
-requests 10 * $max from Twitter.
-
-=cut
-sub check_tweets
-{
-	my $self = shift;
-	my $tweets = undef;
-
-	# Ask for 10 times as many tweets as we will ever say, but no more than
-	# 200
-	my $max = $config{max_tweets} >= 20 ? 200 : $config{max_tweets} * 10;
-	my $count = 0;
-
-	# Ask for the timeline of friend's statuses, only since the last tweet
-	# if we know its id
-	if ($last_tweet != 0) {
-		$tweets = $twit->friends_timeline({
-				since_id => $last_tweet,
-				count    => $max,
-		});
-	} else {
-		$tweets = $twit->friends_timeline({
-				count    => $max,
-			});
-	}
-
-	if ($twit->http_code != 200) {
-		irc_debug("twitter->friend_timelines() failed: %s",
-			$twit->http_message);
-		return;
-	}
-
-=pod
-
-$tweets should now be a reference to an array of:
+	foreach my $f (keys %friends) {
+		# Skip skills training that we've learnt about
+		next if defined $friends{$f}->{skill};
 
-          {
-            'source' => 'web',
-            'favorited' => $VAR1->[0]{'favorited'},
-            'truncated' => $VAR1->[0]{'favorited'},
-            'created_at' => 'Tue Oct 28 22:22:14 +0000 2008',
-            'text' => '@deltafan121 Near Luton, which is just outside London.',
-            'user' => {
-                        'location' => 'Bedfordshire, United Kingdom',
-                        'followers_count' => 10,
-                        'profile_image_url' => 'http://s3.amazonaws.com/twitter_production/profile_images/62344418/SP_A0089_2_normal.jpg',
-                        'protected' => $VAR1->[0]{'favorited'},
-                        'name' => 'Robert Leverington',
-                        'url' => 'http://robertleverington.com/',
-                        'id' => 14450923,
-                        'description' => '',
-                        'screen_name' => 'roberthl'
-                      },
-            'in_reply_to_user_id' => 14662919,
-            'id' => 979630447,
-            'in_reply_to_status_id' => 979535561
-          }
-=cut
-
-=pod
-But I guess we better check, since this happened one time at band camp:
-
-Tue Nov 18 07:58:41 2008| *** twitter->friend_timelines() failed: Can't connect to twitter.com:80 (connect: timeout)
-Tue Nov 18 08:03:41 2008| *** twitter->friend_timelines() failed: Can't connect to twitter.com:80 (connect: timeout)
-Tue Nov 18 08:08:50 2008| *** twitter->friend_timelines() failed: read timeout
-Tue Nov 18 08:13:41 2008| *** twitter->friend_timelines() failed: Can't connect to twitter.com:80 (connect: timeout)
-Tue Nov 18 08:18:41 2008| *** twitter->friend_timelines() failed: Can't connect to twitter.com:80 (connect: timeout)
-Tue Nov 18 08:23:43 2008| *** twitter->friend_timelines() failed: Can't connect to twitter.com:80 (connect: timeout)
-Not an ARRAY reference at ./twitfolk.pl line 494.
-=cut
+		my $skill = $friends{$f}->{char}->skill->in_training;
+		# Nothing training
+		next unless $skill;
 
-	if (ref($tweets) ne "ARRAY") {
-		irc_debug("twitter->friend_timelines() didn't return an arrayref!");
-		return;
-	}
-
-	irc_debug("Got %u new tweets", scalar @{ $tweets });
-
-	# Iterate through them all, sorted by id low to high
-	foreach my $tweet (sort { $a->{id} <=> $b->{id} } @{ $tweets }) {
-		if ($count >= $config{max_tweets}) {
-			irc_debug("Already did %u tweets, stopping there", $count);
-			last;
-		}
-
-		if (lc($tweet->{user}->{screen_name}) eq 'bitfolk') {
-			irc_debug("Skipping tweet from myself");
-			next;
-		}
+		irc_debug("Character %s is training %s (%s)",
+				  $friends{$f}->{char}->name, $skill->name,
+				  $skill->time_remaining);
 
-		if ($tweet->{id} <= $last_tweet) {
-			# Why does Twitter still return tweets that are <= since_id?
-			irc_debug("Tweet %lu: ignored as somehow <= %lu !?",
-				$tweet->{id}, $last_tweet);
-			next;
-		}
-
-		my $screen_name = lc($tweet->{user}->{screen_name});
-		my $text = decode_entities($tweet->{text});
-		my $nick;
-
-		if (! exists($friends{$screen_name})) {
-			irc_debug("I don't have a nickname for Twitter user %s!",
-				$screen_name);
-			$nick = $screen_name;
-		} else {
-			$nick = $friends{$screen_name}->{nick};
-		}
-
-		irc_debug("Tweet %lu: [%s] %s", $tweet->{id}, $screen_name, $text);
-
+		my $text = $skill->name;
 		if ($text =~ /[\n\r]/) {
-			irc_debug("Tweet %lu contains dangerous characters; removing!",
-					  $tweet->{id});
 			$text =~ s/[\n\r]/ /g;
 		}
 
-		$self->notice('#' . $config{channel}, sprintf("[%s] %s", $nick,
+		$self->notice('#' . $config{channel}, sprintf("[%s] %s", $friends{$f}->{nick},
 				encode("utf8", $text)));
-
-		# Save the highest (most recent) id for next time
-		$last_tweet = $tweet->{id} if ($tweet->{id} > $last_tweet);
-		$count++;
 	}
-
-	# Save the new id to the last_tweet file if there were any tweets
-	update_last_tweet($last_tweet) if ($count);
 }
 
-=pod
-
-Read the last tweet id from a file so that no tweets should be repeated
-
-=cut
-sub init_last_tweet
-{
-	return 0 if (! -f $config{tweet_id_file});
-
-	open(LT, "< $config{tweet_id_file}") or die "Couldn't open tweet_id_file: $!";
-
-	my $id = 0;
-
-	while (<LT>) {
-		if (/^(\d+)/) {
-			$id = $1;
-			last;
-		} else {
-			die "Weird format $_ in tweet_id_file";
-		}
-	}
-
-	close(LT) or warn "Something weird when closing tweet_id_file: $!";
-
-	irc_debug("Last tweet id = %lu", $id);
-
-	return $id;
-}
-
-=pod
-
-Save the id of the most recent tweet so that it won't be repeated should
-the bot crash or whatever
-
-=cut
-sub update_last_tweet
-{
-	my $id = shift;
-
-	open(LT, "> $config{tweet_id_file}") or die "Couldn't open tweet_id_file: $!";
-	print LT "$id\n";
-	close(LT) or warn "Something weird when closing tweet_id_file: $!";
-}
-
-
 END {
 	cleanup_and_die();
 }