The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;
use lib 'lib';

use Data::Dumper;
use Getopt::Long;
use POE qw(Component::Client::Twitter Component::Server::IRC Component::TSTP);
use YAML;

GetOptions('-c=s' => \my $conf, '--quiet' => \my $quiet);
$conf or die "Usage: twitter2ircd.pl -c=config.yaml\n";

my $config = YAML::LoadFile($conf) or die $!;

if ($quiet) {
    close STDIN;
    close STDOUT;
    close STDERR;
    exit if fork;
} else {
    # for Ctrl-Z
    POE::Component::TSTP->create();
}

my $ircd = POE::Component::Server::IRC->spawn(
    alias  => 'ircd',
    config => {
        servername => $config->{irc}->{servername},
        nicklen    => 15,
        network    => 'SimpleNET'
    },
);

my $twitter = POE::Component::Client::Twitter->spawn(%{ $config->{twitter} });

POE::Session->create(
    inline_states => {
        _start   => \&_start,
        ircd_daemon_nick => \&ircd_nick,
        ircd_daemon_join => \&ircd_join,
        ircd_daemon_part => \&ircd_part,
        ircd_daemon_quit => \&ircd_quit,
#        ircd_daemon_privmsg => \&ircd_privmsg,
        ircd_daemon_public  => \&ircd_public,

        'twitter.update_success' => \&twitter_update_success,
        'twitter.friend_timeline_success' => \&twitter_friend_timeline_success,
        'twitter.response_error' => \&twitter_error,

        bot_join => \&bot_join,
        delay_friend_timeline => \&delay_friend_timeline,
    },
    options => { trace => 0 },
    heap => { ircd => $ircd, twitter => $twitter, config => $config },
);

$poe_kernel->run();
exit 0;

sub _start {
    my ($kernel,$heap) = @_[KERNEL,HEAP];
    my $conf = $heap->{config}->{irc};

    # register ircd to receive events
    $heap->{ircd}->yield( 'register' );
    $heap->{twitter}->yield('register');
    $heap->{ircd}->add_auth(
        mask => $conf->{mask},
        password => $conf->{password}
    );
    $heap->{ircd}->add_listener( port => $conf->{serverport} || 6667 );

    # add super user
    $heap->{ircd}->yield(add_spoofed_nick => { nick => $conf->{botname} });
    $heap->{ircd}->yield(daemon_cmd_join => $conf->{botname}, $conf->{channel});
    $kernel->delay('delay_friend_timeline', 5);

    $heap->{nicknames} = {};
    $heap->{joined}    = 0;
    $heap->{stack}     = [];

    undef;
}

sub delay_friend_timeline {
    my($kernel, $heap) = @_[KERNEL, HEAP];
    $heap->{twitter}->yield('friend_timeline');
}

sub bot_join {
    my($kernel, $heap, $nick, $ch) = @_[KERNEL, HEAP, ARG0, ARG1];

    return if $heap->{nicknames}->{$nick};
    $heap->{ircd}->yield(add_spoofed_nick => { nick => $nick });
    $heap->{ircd}->yield(daemon_cmd_join => $nick, $ch);
    $heap->{nicknames}->{$nick} = 1;
}

sub ircd_nick {
    my($kernel, $heap, $nick, $host) = @_[KERNEL, HEAP, ARG0, ARG5];
    my $conf = $heap->{config}->{irc};

    return if $nick eq $conf->{botname};
    return if $heap->{nick_change} || '';
    if (($host || '') eq $conf->{servername}) {
        $heap->{ircd}->_daemon_cmd_join($nick, $conf->{channel});
        $heap->{nick_change} = 1;
        $heap->{ircd}->_daemon_cmd_nick($nick, $conf->{nickname});
        delete $heap->{nick_change};
    }

    $heap->{nick} = $nick;
}

sub ircd_join {
    my($kernel, $heap, $user, $ch) = @_[KERNEL,HEAP,ARG0,ARG1];
    my $conf = $heap->{config}->{irc};

    return unless my($nick) = $user =~ /^([^!]+)!/;
    return if $heap->{nicknames}->{$nick};
    return if $nick eq $conf->{botname};
    if ($ch eq $conf->{channel}) {
        $heap->{joined} = 1;
        $heap->{twitter}->yield(update => 'twitter2irc.pl join');

        for my $data (@{ $heap->{stack} }) {
            $heap->{ircd}->yield(daemon_cmd_privmsg => $data->{name}, $conf->{channel}, $data->{text});
        }
        $heap->{stack} = [];
        return;
    }
    $heap->{ircd}->_daemon_cmd_part($nick, $ch);
}

sub ircd_part {
    my($kernel, $heap, $user, $ch) = @_[KERNEL,HEAP,ARG0,ARG1];
    my $conf = $heap->{config}->{irc};

    return unless my($nick) = $user =~ /^([^!]+)!/;
    return if $heap->{nicknames}->{$nick};
    return if $nick eq $conf->{botname};

    if ($ch eq $conf->{channel}) {
        $heap->{joined} = 0;
        $heap->{twitter}->yield(update => 'twitter2irc.pl part');
    }
}

sub ircd_quit {
    my($kernel, $heap, $user) = @_[KERNEL,HEAP,ARG0];
    my $conf = $heap->{config}->{irc};

    return unless my($nick) = $user =~ /^([^!]+)!/;
    return if $heap->{nicknames}->{$nick};
    return if $nick eq $conf->{botname};
    $heap->{joined} = 0;
    $heap->{twitter}->yield(update => 'twitter2irc.pl quit');
}

sub ircd_public {
    my($kernel, $heap, $user, $channel, $text) = @_[KERNEL, HEAP, ARG0, ARG1, ARG2];
    my $conf = $heap->{config}->{irc};

    my $nick = ( $user =~ m/^(.*)!/)[0];
    $heap->{twitter}->yield(update => $text);
}


sub twitter_update_success {
    my($kernel, $heap, $ret) = @_[KERNEL, HEAP, ARG0];
    my $conf = $heap->{config}->{irc};
    $heap->{ircd}->yield(daemon_cmd_notice => $conf->{botname}, $conf->{channel}, $ret->{text});
}

sub twitter_friend_timeline_success {
    my($kernel, $heap, $ret) = @_[KERNEL, HEAP, ARG0];
    my $conf = $heap->{config}->{irc};

    $ret = [] unless $ret;
    for my $line (reverse @{ $ret }) {
        my $name = $line->{user}->{screen_name};
        my $text = $line->{text};

        unless ($heap->{nicknames}->{$name}) {
            $heap->{ircd}->yield(add_spoofed_nick => { nick => $name });
            $heap->{ircd}->yield(daemon_cmd_join => $name, $conf->{channel});
            $heap->{nicknames}->{$name} = 1;
        }

        next if $heap->{config}->{twitter}->{screenname} eq $name;
        if ($heap->{joined}) {
            $heap->{ircd}->yield(daemon_cmd_privmsg => $name, $conf->{channel}, $text);
        } else {
            push @{ $heap->{stack} }, { name => $name, text => $text }
        }
    }
    $kernel->delay('delay_friend_timeline', $heap->{config}->{twitter}->{retry});
}

sub twitter_error {
    my($kernel, $heap, $res) = @_[KERNEL, HEAP, ARG0];
    my $conf = $heap->{config}->{irc};
    $heap->{ircd}->yield(daemon_cmd_notice => $conf->{botname}, $conf->{channel}, 'Twitter error');
}

__END__

config.yaml example

irc:
  servername: twitter.irc
  serverport: 6667
  botname: twitter
  nickname: chatname
  password: password
  mask: '*@*'
  channel: '#twitter'
twitter:
  screenname: twittername
  username: twitter id
  password: twitter password
  retry: 300