Call notification via IRC

From JoatWiki
Jump to: navigation, search

Call notification via IRC

(5 Jul 2009) - Following are my notes for setting up call notification via IRC. Please note that the following will probably change as I tweak the code and add features.

Wish list

  • Notify a user in IRC when someone tries to call them
  • User doesn't have to be logged into any specific channel, only the same IRC server
  • IRC bot caches messages until user is logged into the IRC server.
  • Leave "room" for future capabilities (explanation later)
  • Include a timestamp in the message

Design

Rather than reinvent the wheel for message storage, I decided to just glue some pieces of already available code together.

I'd messed around with IRC bots before and had written a bot with a MySQL backend. It was simple to adapt this code to create a message bot. It only needed a function to watch for specific users (code below).

The AGI script took a bit more creativity (but not much). All that was needed was code to grab the Caller ID to generate a message and push it into the message queue in the MySQL database (code also below).

The Dial Plan

From the piece of my dialplan that rings my phone:

exten => 1705,hint,SIP/1705
exten => 1705,1,Wait(1)
exten => 1705,2,AGI(hoodahek_dbhandle.pl,${CALLERID(name)},${CALLERID(num)},${EXTEN})
exten => 1705,3,Dial(SIP/1705,20,tT)
exten => 1705,4,AGI(qluphone,joat,${CALLERID(name)},${CALLERID(num)})
exten => 1705,5,VoiceMail(1705,u)
exten => 1705,6,Hangup
exten => 1705,104,AGI(qluphone,joat,${CALLERID(name)},${CALLERID(num)})
exten => 1705,105,VoiceMail(1705,b)

The lines that contain "qluphone" are the ones under discussion. Because I don't want to be notified about calls that I did answer, I've dropped them in front of the VoiceMail lines (i.e., if the call goes to voicemail, notifiy me).

The AGI arguments are basically: the name of the AGI script (i.e., qluephone), my name in IRC (i.e., joat), and the Caller ID info of the caller.

The AGI Script

use DBI;

$name=$ARGV[0];
$caller=$ARGV[1];
$number=$ARGV[2];

# info for connecting to the database
$sql_database_name="qlu";
$sql_database_host="192.168.2.175";
$sql_database_port="3306";
$sql_connect_string="DBI:mysql:$sql_database_name:$sql_database_host:$sql_database_port";
$sql_username="myuser";
$sql_password="mypass";

connect_to_database();
$sql="insert into messages(user,message) values('$name','$caller tried to call you from $number')";
process_sql();
disconnect_from_database();

# connect to database
sub connect_to_database {
       $dbh = DBI->connect($sql_connect_string,$sql_username,$sql_password) or die "Connecting : $DBI::errstr\n ";
}

# disconnect from database
sub disconnect_from_database {
       $dbh->disconnect();
}

sub process_sql {
       $sth = $dbh->prepare("$sql") or die "preparing: ",$dbh->errstr;
       $sth->execute or die "executing: ", $dbh->errstr;
}


The IRC Bot

Basically, this bot requires some sort of activity on the IRC channel as a "timing" source to trigger a database search and deliver messages. The low level IRC traffic (i.e., PINGs and message traffic) work well for this. In short the bot's main loop "blocks" until there's IRC activity but there's enough background activity to cause the bot to deliver notifications within minutes.

#!/usr/bin/perl -w

use IO::Socket;
use DBI;

# IRC server info
my $server = "192.168.2.175";
my $nick = "qlu";
my $login = "qlu";

# Channel on the IRC server to use
# Not needed for targeted messages
# Needed for generic (status) messages
my $channel = "#geeksanon";

# Database server info
$sql_database_name="qlu";
$sql_database_host="192.168.2.175";
$sql_database_port="3306";
$sql_connect_string="DBI:mysql:$sql_database_name:$sql_database_host:$sql_database_port";
$sql_username="myuser";
$sql_password="mypass";

# Connect to the IRC server.
my $sock = new IO::Socket::INET(PeerAddr => $server,
                               PeerPort => 6667,
                               Proto => 'tcp') or
                                   die "Can't connect\n";

# IRC log on
print $sock "NICK $nick\r\n";
print $sock "USER $login 8 * :Perl IRC Hacks Robot\r\n";

# Read lines from the server until it tells us we have connected.
while (my $input = <$sock>) {
    # Check the numerical responses from the server.
    if ($input =~ /004/) {
        # We are now logged in.
        last;
    }
    elsif ($input =~ /433/) {
        die "Nickname is already in use.";
    }
}

# Join the channel.
print $sock "JOIN $channel\r\n";

# Read lines from the server until end of MOTD
while (my $input = <$sock>) {
    # Check the numerical responses from the server.
    if ($input =~ /End of \/NAMES list./) {
        # We are now logged in.
        last;
    }
}

# Keep reading lines from the server.
while (my $input = <$sock>) {
    chop $input;
    if ($input =~ /^PING(.*)$/i) {
        # We must respond to PINGs to avoid being disconnected.
        print $sock "PONG $1\r\n";
    #} else {
        }
        # Print the raw line received by the bot.
        print "$input\n";
        connect_to_database();
        $sql="select id,user,message from messages";
        process_sql();
        my $count=0;
        while($row=$sth->fetchrow_hashref) {
                $id[$count]=$row->{"id"};
                $user[$count]=$row->{"user"};
                $message[$count]=$row->{"message"};
                $count++;
        }
        disconnect_from_database();
        for($a=0;$a<$count;$a++) {
                #my $msg=$row->{"message"};
                if ($user[$a] ne "") {
                        print $sock "PRIVMSG $user[$a] : $message[$a]\n";
                } else {
                        print $sock "PRIVMSG $channel : $message[$a]\n";
                }
                print "PRIVMSG $channel $message[$a]\n";
        }
        if($count>0) {
                connect_to_database();
                for($a=0;$a<$count;$a++) {
                        $sql="delete from messages where id=$id[$a]";
                        process_sql();
                }
                disconnect_from_database();
        }
    #}
}

# connect to database
sub connect_to_database {
        $dbh = DBI->connect($sql_connect_string,$sql_username,$sql_password) or die "Connecting : $DBI::errstr\n ";
}

# disconnect from database
sub disconnect_from_database {
        $dbh->disconnect();
}

sub process_sql {
        $sth = $dbh->prepare("$sql") or die "preparing: ",$dbh->errstr;
        $sth->execute or die "executing: ", $dbh->errstr;
}





Comments:

Leave a Comment

Personal tools