1. #!/usr/bin/perl -w
  2. use strict;
  3. use feature "state";
  4. use Data::Dumper;
  5. use Storable;
  6. use DateTime;
  7. use Switch;
  8. use Time::Format;
  9. use WebService::GData::YouTube;
  10. use LWP::Simple;
  11. use Data::Validate::URI qw(is_web_uri);
  12. use POE;
  13. use POE::Component::IRC;
  14. use POE::Component::IRC::Plugin::Console;
  15. use POE::Component::IRC::Plugin::NickServID;
  16. use POE::Component::IRC::Plugin::BotAddressed;
  17. use POE::Component::IRC::Plugin::Logger;
  18. use WWW::Wikipedia;
  19. use WWW::YouTube::Info;
  20. use Google::Search;
  21. use DBI;
  22.  
  23. my $dbh = DBI->connect("dbi:SQLite:dbname=raven.db","","") or die $DBI::errstr;
  24. my @tables = $dbh->tables();
  25. my @help = (
  26.     ['.8ball','Predict the future with yes/no questions. - ex. .8ball Is Raven awesome?'],
  27.     ['.tell','Leave a message for a user - ex. .tell user The server is down again.'],
  28.     ['.google','Search Google and return the first result'],
  29.     ['.youtube','Search YouTube and return the first result'],
  30.     ['.wiki','Search for a wiki on the selected topic'],
  31.     ['.down','Check if a website is down.'],
  32.     ['.quote','Record or play back a quote. ".quote" random quote from any user, ".quote user" random quote for the user, ".quote user Message to be quoted" save a new quote'],
  33.     ['.info','Record or play back stored info'],
  34.     ['.define','Look up a definition of a word'],
  35.     ['.urban','Search the Urban Dictionary'],
  36.     ['.devbukkit','Search dev.bukkit.org for plugins'],
  37.     ['.seen','Show last activity for a user'],
  38. );
  39. my @ophelp = (
  40.     ['join','Join a channel'],
  41.     ['part','Leave a channel'],
  42.     ['op','Add an op'],
  43.     ['deop','Remove an op'],
  44.     ['speakas','Make the bot say something in a channel or to a user - ex. /msg RavenBot #MyChannel What are you doing, Dave?'],
  45.     ['emoteas','Make the bot perform a CTCP ACTION [/me] in a channel or to a user chat'],
  46.     ['shutdown','Terminate the bot'],
  47. );
  48.  
  49. my $URLValidator = Data::Validate::URI->new();
  50. # Create tables (if they don't exist)
  51. $dbh->do("CREATE TABLE IF NOT EXISTS Channels(name)");
  52. $dbh->do("CREATE TABLE IF NOT EXISTS Ops(name)");
  53. $dbh->do("CREATE TABLE IF NOT EXISTS Tells(sender, rcpt, channel, message)");
  54. $dbh->do("CREATE TABLE IF NOT EXISTS Info(key PRIMARY KEY, data)");
  55. $dbh->do("CREATE TABLE IF NOT EXISTS Quotes(user, data)");
  56. $dbh->do("CREATE TABLE IF NOT EXISTS LastSeen(user PRIMARY KEY, timestamp)");
  57. # Prepare Database statements
  58. my $addQuote = $dbh->prepare("INSERT INTO Quotes(user, data) VALUES (?, ?);");
  59. my $getUserQuote = $dbh->prepare("SELECT data FROM Quotes WHERE user = ? ORDER BY RANDOM () LIMIT 1;");
  60. my $getAnyQuote = $dbh->prepare("SELECT user, data FROM Quotes ORDER BY RANDOM () LIMIT 1;");
  61. my $updateLastSeen = $dbh->prepare("REPLACE INTO LastSeen(user, timestamp) VALUES (?, ?);");
  62. my $getLastSeen = $dbh->prepare("SELECT timestamp FROM LastSeen WHERE user = ?;");
  63. my $updateInfo = $dbh->prepare("REPLACE INTO Info(key, data) VALUES (?, ?);");
  64. my $getInfo = $dbh->prepare("SELECT data FROM Info WHERE key = ?;");
  65. my $addTell = $dbh->prepare("INSERT INTO Tells(sender, rcpt, channel, message) VALUES (?, ?, ?, ?);");
  66. my $getTells = $dbh->prepare("SELECT rowid, sender, channel, message FROM Tells WHERE rcpt = ?;");
  67. my $removeTells = $dbh->prepare("DELETE FROM Tells WHERE rcpt = ?;");
  68. my $addChannel = $dbh->prepare("REPLACE INTO Channels (name) VALUES (?);");
  69. my $removeChannel = $dbh->prepare("DELETE FROM Channels WHERE name = ?;");
  70. my $addOp = $dbh->prepare("REPLACE INTO Ops (name) VALUES (?);");
  71. my $removeOp = $dbh->prepare("DELETE FROM Ops WHERE name = ?;");
  72. # Finished setup
  73.  
  74. # Color contants
  75. my $WHITE = "\x0300";
  76. my $BLACK = "\x0301";
  77. my $BLUE = "\x0302";
  78. my $GREEN = "\x0303";
  79. my $RED = "\x0304";
  80. my $BROWN = "\x0305";
  81. my $PURPLE = "\x0306";
  82. my $ORANGE = "\x0307";
  83. my $YELLOW = "\x0308";
  84. my $LIGHT_GREEN = "\x0309";
  85. my $TEAL = "\x0310";
  86. my $LIGHT_CYAN = "\x0311";
  87. my $LIGHT_BLUE = "\x0312";
  88. my $PINK = "\x0313";
  89. my $GREY = "\x0314";
  90. my $LIGHT_GREY = "\x0315";
  91. my $NORMAL = "\x0f";
  92.  
  93. my @magic8ball = (
  94.     qq<$GREEN Signs point to yes.>,
  95.     qq<$GREEN Yes.>,
  96.     qq<$YELLOW Reply hazy, try again.>,
  97.     qq<$GREEN Without a doubt.>,
  98.     qq<$RED My sources say no.>,
  99.     qq<$GREEN As I see it, yes.>,
  100.     qq<$GREEN You may rely on it.>,
  101.     qq<$YELLOW Concentrate and ask again.>,
  102.     qq<$RED Outlook not so good.>,
  103.     qq<$GREEN It is decidedly so.>,
  104.     qq<$YELLOW Better not tell you now.>,
  105.     qq<$RED Very doubtful.>,
  106.     qq<$GREEN Yes - definitely.>,
  107.     qq<$GREEN It is certain.>,
  108.     qq<$YELLOW Cannot predict now.>,
  109.     qq<$GREEN Most likely.>,
  110.     qq<$YELLOW Ask again later.>,
  111.     qq<$RED My reply is no.>,
  112.     qq<$GREEN Outlook good.>,
  113.     qq<$RED Do not count on it.>,
  114. );
  115.  
  116. my ($irc) = POE::Component::IRC->spawn();
  117. my @channels = &LoadChannels;
  118. my @ops = &LoadOps;
  119.  
  120. POE::Session->create(
  121.     inline_states => {
  122.         _start => \&bot_start,
  123.         irc_001 => \&on_connect,
  124.         irc_public => \&on_public,
  125.         irc_msg => \&on_private,
  126.         irc_join => \&on_join,
  127.         irc_bot_mentioned => \&on_mention,
  128.         irc_kick => \&on_kick,
  129.         irc_disconnected => \&on_disconnect,
  130.     },
  131. );
  132.  
  133. sub bot_start {
  134.     $irc->yield(register => "all");
  135.     my $nick = "RavenBot";
  136.     $irc->yield(connect => {
  137.             Nick => $nick,
  138.             Username => 'ravenbot',
  139.             Ircname => q{MCPortCentral's RavenBot},
  140.             Server => 'irc.esper.net',
  141.             Port => '6667',
  142.             Flood => 'true',
  143.         }
  144.     );
  145. #$irc->plugin_add( 'Console', POE::Component::IRC::Plugin::Console->new( bindport => 1234, password => 'test' ));
  146.     $irc->plugin_add( 'NickServID', POE::Component::IRC::Plugin::NickServID->new( Password => 'Raven!@)(' ) );
  147.     $irc->plugin_add( 'BotAddressed', POE::Component::IRC::Plugin::BotAddressed->new() );
  148.     $irc->plugin_add( 'Logger', POE::Component::IRC::Plugin::Logger->new( Path => '/opt/RavenBot/ravenbot/logs', DCC => 0, Private => 1, Public => 1, ) );
  149. }
  150.  
  151. sub on_connect {
  152.     $irc->yield(join => $_) for @channels;
  153.     return;
  154. }
  155.  
  156. sub on_disconnect {
  157.     &bot_start;
  158.     return;
  159. }
  160.  
  161. sub on_public {
  162.     my ($kernel, $who, $where, $msg) = @_[KERNEL, ARG0, ARG1, ARG2];
  163.     my $nick = (split /!/, $who)[0];
  164.     my $channel = $where->[0];
  165.     my $dt = DateTime->now( time_zone=> 'UTC' );
  166.     my $ts = $dt->epoch;
  167.     my $isOp = 0;
  168.     if ($msg =~ /youtube\.com/) {
  169.         my @matches = ($msg =~ m/v\=.*/g);
  170.         my $videoid = substr($matches[0],2,11);
  171.         my $yt = new WebService::GData::YouTube();
  172.         $yt->query->q($videoid)->limit(1,0);
  173.         my $videos = $yt->search_video();
  174.         if (!(defined $videos)) { return }
  175.         my $length = $$videos[0]->{_feed}{'media$group'}{'yt$duration'}{'seconds'};
  176.         my $time = &ConvertFromSeconds($length);
  177.         my $url = $$videos[0]->{_feed}{link}[0]{href};
  178.         $url =~ s/\&feature=youtube_gdata//g;
  179.         my $uploader = $$videos[0]->{_feed}{'media$group'}{'media$credit'}[0]{'yt$display'};
  180.         my $uploadedon = substr($$videos[0]->{_feed}{'media$group'}{'yt$uploaded'}{text}, 0, 10);
  181.         my $views = &AddCommas($$videos[0]->{_feed}{'yt$statistics'}{viewCount});
  182.         eval{ $irc->yield(privmsg => $channel, $nick . qq<: \x02> . $$videos[0]->title . qq<\x02 - length: \x02> . $time . qq<\x02 - > . $url . qq< - \x02> . $views  . qq<\x02 views - uploaded by: \x02> . $uploader . qq<\x02 on \x02> . $uploadedon . qq<\x02> ); };
  183.         $irc->yield(privmsg => $channel, $nick . qq<: Error while finding information>) if $@;
  184.     }
  185.     foreach (@ops) {
  186.         if ( $nick eq $_ ) { $isOp = 1; }
  187.     }
  188.     $getTells->execute($nick);
  189.     while (my $row = $getTells->fetchrow_hashref){
  190.         $irc->yield(notice => $nick, $$row{'sender'} . ' in ' . $$row{'channel'} . '> ' . $$row{'message'});
  191.         $dbh->do("DELETE FROM Tells where rowid = " . $$row{'rowid'} . ";");
  192.     }
  193.     $updateLastSeen->execute($nick, $ts);
  194.     if ($msg =~ /^\./) {
  195.         my ($command, @cmdargs) = (split / /, $msg);
  196.         switch ($command)
  197.         {
  198.             case '.8ball' {
  199.                 my $response = $magic8ball[rand @magic8ball];
  200.                 my $question = "";
  201.                 for (my $i = 0; $i <= $#cmdargs; $i++) {
  202.                     $question .= $cmdargs[$i] . " ";
  203.                 }
  204.                 chop($question);
  205.                 $irc->yield(privmsg => $channel, $nick . q< asked "> . $question . q<" - > . $response);
  206.             }
  207.             case /\.t($|ell$)/ {
  208.                 my $numArgs = @cmdargs;
  209.                 if ($numArgs == 0) {
  210.                     $irc->yield(privmsg => $channel, qq<$nick: Who did you want to tell and what?>);
  211.                     return;
  212.                 }
  213.                 if ($numArgs == 1) {
  214.                     $irc->yield(privmsg => $channel, qq<$nick: What did you want to tell that user?>);
  215.                 } else {
  216.                     my $rcpt = $cmdargs[0];
  217.                     my $msg = "";
  218.                     for (my $i =1; $i <= $#cmdargs; $i++) {
  219.                         $msg .= $cmdargs[$i] . " ";
  220.                     }
  221.                     chop($msg);
  222.                     $addTell->execute($nick, $rcpt, $channel, $msg);
  223.                     $irc->yield(privmsg => $channel, qq<$nick: \x02$rcpt\x02 will be notified on their next activity.>);
  224.                 }
  225.             }
  226.             case /\.g($|oogle$)/ {
  227.                 my $query = "";
  228.                 for (my $i = 0; $i <= $#cmdargs; $i++) {
  229.                     $query .= $cmdargs[$i] . " ";
  230.                 }
  231.                 chop($query);
  232.                 my $search = Google::Search->Web( query => $query );
  233.                 eval{
  234.                     my $result = $search->first;
  235.                     my $content;
  236.                     if (defined $result) {
  237.                         $content = $result->content;
  238.                     } else {
  239.                         $content = "No results found";
  240.                     }
  241.                     $content =~ s|<.+?>||g;
  242.                     $irc->yield(privmsg => $channel, $nick . q<: > . $result->unescapedUrl . qq< - \x02> . $result->titleNoFormatting . qq<\x02: "> . $content . q<">);
  243.                 };
  244.                 $irc->yield(privmsg => $channel, $nick . qq<: Error while finding information>) if $@;
  245.             }
  246.             case /\.(yt$|youtube$)/ {
  247.                 my $yt = new WebService::GData::YouTube();
  248.                 my $query = "";
  249.                 for (my $i = 0; $i <= $#cmdargs; $i++) {
  250.                     $query .= $cmdargs[$i] . " ";
  251.                 }
  252.                 chop($query);
  253.                 eval{
  254.                     $yt->query->q($query)->limit(1,0);
  255.                     my $videos = $yt->search_video();
  256.                     my $length = $$videos[0]->{_feed}{'media$group'}{'yt$duration'}{'seconds'};
  257.                     my $time = &ConvertFromSeconds($length);
  258.                     my $url = $$videos[0]->{_feed}{link}[0]{href};
  259.                     $url =~ s/\&feature=youtube_gdata//g;
  260.                     my $uploader = $$videos[0]->{_feed}{'media$group'}{'media$credit'}[0]{'yt$display'};
  261.                     my $uploadedon = substr($$videos[0]->{_feed}{'media$group'}{'yt$uploaded'}{text}, 0, 10);
  262.                     my $views = &AddCommas($$videos[0]->{_feed}{'yt$statistics'}{viewCount});
  263.                     $irc->yield(privmsg => $channel, $nick . qq<: \x02> . $$videos[0]->title . qq<\x02 - length: \x02> . $time . qq<\x02 - > . $url . qq< - \x02> . $views  . qq<\x02 views - uploaded by: \x02> . $uploader . qq<\x02 on \x02> . $uploadedon . qq<\x02> );
  264.                 };
  265.                 $irc->yield(privmsg => $channel, $nick . qq<: Error while finding information>) if $@;
  266.             }
  267.             case ".wiki" {
  268.                 my $query = "wiki ";
  269.                 for (my $i = 0; $i <= $#cmdargs; $i++) {
  270.                     $query .= $cmdargs[$i] . " ";
  271.                 }
  272.                 chop($query);
  273.                 eval{
  274.                     my $search = Google::Search->Web( query => $query );
  275.                     my $result = $search->first;
  276.                     my $content = $result->content;
  277.                     $content =~ s|<.+?>||g;
  278.                     $irc->yield(privmsg => $channel, $nick . q<: > . $result->unescapedUrl . qq< - \x02> . $result->titleNoFormatting . qq<\x02: "> . $content . q<">);
  279.                 };
  280.                 $irc->yield(privmsg => $channel, $nick . qq<: Error while finding information>) if $@;
  281.             }          
  282.             case ".down" {
  283.                 my $url = $cmdargs[0];
  284.                 if ($URLValidator->is_web_uri( $url )) {
  285.                     my $status = "";
  286.                     if (! head($url) ){
  287.                         $status = "$url appears to be " . $RED . "down" . $NORMAL . "!";
  288.                     } else {
  289.                         $status = "$url appears to be " . $GREEN . "up" . $NORMAL . "!";
  290.                     }
  291.                     $irc->yield(privmsg => $channel, $nick . qq<: $status>);
  292.                 } else {
  293.                     $irc->yield(privmsg => $channel, $nick . qq<: $url does not appear to be a valid URL.>);
  294.                 }
  295.             }
  296.             case /\.q($|uote$)/ {
  297.                 my $numArgs = @cmdargs;
  298.                 if ($numArgs == 0) {
  299.                     $getAnyQuote->execute;
  300.                     my $result = $getAnyQuote->fetchrow_hashref;
  301.                     $irc->yield(privmsg => $channel, '<' . $$result{'user'} . '> ' . $$result{'data'});
  302.                     return;
  303.                 }
  304.                 if ($numArgs == 1) {
  305.                     $getUserQuote->execute($cmdargs[0]);
  306.                     my $result = $getUserQuote->fetch;
  307.                     if (!(defined $result)) {
  308.                         $irc->yield(privmsg => $channel, 'No quotes found for ' . $cmdargs[0] . '.');
  309.                         return;
  310.                     }
  311.                     $irc->yield(privmsg => $channel, '<' . $cmdargs[0] . '> ' . $$result[0]);
  312.                 } else {
  313.                     my $user = $cmdargs[0];
  314.                     my $quote = "";
  315.                     for (my $i =1; $i <= $#cmdargs; $i++) {
  316.                         $quote .= $cmdargs[$i] . " ";
  317.                     }
  318.                     chop($quote);
  319.                     $addQuote->execute($user, $quote);
  320.                     $irc->yield(privmsg => $channel, qq<$nick: Quote added to database.>);
  321.                 }
  322.             }
  323.             case /\.i($|nfo$)/ {
  324.                 my $numArgs = @cmdargs;
  325.                 if ($numArgs == 0) {
  326.                     $irc->yield(privmsg => $channel, qq<$nick: No key specified.>);
  327.                     return;
  328.                 }
  329.                 if ($numArgs == 1) {
  330.                     $getInfo->execute($cmdargs[0]);
  331.                     my $result = $getInfo->fetch;
  332.                     $irc->yield(privmsg => $channel, qq<\x02> . $cmdargs[0] . qq<\x02 - > . $$result[0]);
  333.                 } else {
  334.                     my $key = $cmdargs[0];
  335.                     my $data = "";
  336.                     for (my $i = 1; $i <= $#cmdargs; $i++) {
  337.                         $data .= $cmdargs[$i] . " ";
  338.                     }
  339.                     chop($data);
  340.                     $updateInfo->execute($key, $data);
  341.                     $irc->yield(privmsg => $channel, qq<$nick: Value set.>);
  342.                 }
  343.             }
  344.             case ".define" {
  345.                 my $query = "define:";
  346.                 for (my $i = 0; $i <= $#cmdargs; $i++) {
  347.                     $query .= $cmdargs[$i] . " ";
  348.                 }
  349.                 chop($query);
  350.                 eval{
  351.                     my $search = Google::Search->Web( query => $query );
  352.                     my $result = $search->first;
  353. #print Dumper($result);
  354.                     my $content = $result->content;
  355.                     $content =~ s|<.+?>||g;
  356.                     $irc->yield(privmsg => $channel, $nick . q<: > . $result->unescapedUrl . qq< - \x02> . $result->titleNoFormatting . qq<\x02: "> . $content . q<">);
  357.                 };
  358.                 $irc->yield(privmsg => $channel, $nick . qq<: Error while finding information>) if $@;
  359.             }
  360.             case ".urban" {
  361.                 my $query = "site:urbandictionary.com ";
  362.                 for (my $i = 0; $i <= $#cmdargs; $i++) {
  363.                     $query .= $cmdargs[$i] . " ";
  364.                 }
  365.                 chop($query);
  366.                 eval{
  367.                     my $search = Google::Search->Web( query => $query );
  368.                     my $result = $search->first;
  369.                     my $content = $result->content;
  370.                     $content =~ s|<.+?>||g;
  371.                     $irc->yield(privmsg => $channel, $nick . q<: > . $result->unescapedUrl . qq< - \x02> . $result->titleNoFormatting . qq<\x02: "> . $content . q<">);
  372.                 };
  373.                 $irc->yield(privmsg => $channel, $nick . qq<: Error while finding information>) if $@;
  374.             }
  375.             case /.(db$|devbukkit$)/ {
  376.                 my $query = "site:dev.bukkit.org ";
  377.                 for (my $i = 0; $i <= $#cmdargs; $i++) {
  378.                     $query .= $cmdargs[$i] . " ";
  379.                 }
  380.                 chop($query);
  381.                 my $search = Google::Search->Web( query => $query );
  382.                 eval{
  383.                     my $result = $search->first;
  384.                     my $content;
  385.                     if (defined $result) {
  386.                         $content = $result->content;
  387.                     } else {
  388.                         $content = "No results found";
  389.                     }
  390.                     $content =~ s|<.+?>||g;
  391.                     $irc->yield(privmsg => $channel, $nick . q<: > . $result->unescapedUrl . qq< - \x02> . $result->titleNoFormatting . qq<\x02: "> . $content . q<">);
  392.                 };
  393.                 $irc->yield(privmsg => $channel, $nick . qq<: Error while finding information>) if $@;
  394.             }
  395.             case ".seen" {
  396.                 my $numArgs = @cmdargs;
  397.                 if ($numArgs >= 1) {
  398.                     my $user = $cmdargs[0];
  399.                     $getLastSeen->execute($user);
  400.                     my $result = $getLastSeen->fetch;
  401.                     if (!(defined $result)){
  402.                         $irc->yield(privmsg => $channel, qq<\x02$user\x02 does not have any known activity.>);
  403.                         return;
  404.                     }
  405.                     my $time = $$result[0];
  406.                     my $lastseen = DateTime->from_epoch ( epoch => $time );
  407.                     my $lsmsg = $lastseen->day . '-' . $lastseen->month_abbr . '-' . $lastseen->year . ' ' . $lastseen->hms . ' UTC';
  408.                     my $duration = &ConvertFromSeconds(($ts - $time));
  409.                     $irc->yield(privmsg => $channel, qq<\x02$user\x02 was last seen $lsmsg ($duration ago).>);
  410.                 }
  411.             }
  412.             case /.(brainfuck$|bf$)/ {
  413.                 $irc->yield(privmsg => $channel, qq<\x02$nick\x02 No esoteric programming languages allowed.>);
  414.             }
  415.             case /.help$/ {
  416.                 for (my $i = 0; $i <= $#help; $i++) {
  417.                     $irc->yield(notice => $nick, $help[$i][0] . ' - ' . $help[$i][1]);
  418.                 }
  419.             }
  420.         }
  421.     }
  422.     return;
  423. }
  424.  
  425. sub on_private {
  426.     my ($kernel, $from, $to, $msg, $identified) = @_[KERNEL, ARG0, ARG1, ARG2, ARG3];
  427.     my $nick = (split /!/, $from)[0];
  428.     my ($command, @cmdargs) = (split / /, $msg);
  429.     my $isOp = 0;
  430.     foreach (@ops) {
  431.         if ( $nick eq $_ ) { $isOp = 1; }
  432.     }
  433.     switch ($command) {
  434.         case 'tables' {
  435.             if ($isOp == 1) {
  436.                 $irc->yield(privmsg => $nick, @tables);
  437.             } else {
  438.                 $irc->yield(privmsg => $nick, "You cannot do that.");
  439.             }
  440.         }
  441.         case 'emoteas' {
  442.             if ($isOp == 1) {
  443.                 my $response;
  444.                 for (my $i = 1; $i <= $#cmdargs; $i++) {
  445.                     $response .= $cmdargs[$i] . " ";
  446.                 }
  447.                 $irc->yield(ctcp => $cmdargs[0], qq<ACTION $response>);
  448.                 $irc->yield(privmsg => $nick, "Sent!");
  449.             } else {
  450.                 $irc->yield(privmsg => $nick, "You cannot do that.");
  451.             }
  452.         }
  453.         case 'speakas' {
  454.             if ($isOp == 1) {
  455.                 my $response;
  456.                 for (my $i = 1; $i <= $#cmdargs; $i++) {
  457.                     $response .= $cmdargs[$i] . " ";
  458.                 }
  459.                 $irc->yield(privmsg => $cmdargs[0], $response);
  460.                 $irc->yield(privmsg => $nick, "Sent!");
  461.             } else {
  462.                 $irc->yield(privmsg => $nick, "You cannot do that.");
  463.             }
  464.         }
  465.         case 'join' {
  466.             if ($isOp == 1) {
  467.                 $irc->yield(join => $cmdargs[0]);
  468.                 $addChannel->execute($cmdargs[0]);
  469.             } else {
  470.                 $irc->yield(privmsg => $nick, "You cannot do that.");
  471.             }
  472.         }
  473.         case 'part' {
  474.             if ($isOp == 1) {
  475.                 $irc->yield(part => $cmdargs[0]);
  476.                 $removeChannel->execute($cmdargs[0]);
  477.             } else {
  478.                 $irc->yield(privmsg => $nick, "You cannot do that.");
  479.             }
  480.         }
  481.         case 'op' {
  482.             if ($isOp == 1) {
  483.                 push(@ops, $cmdargs[0]);
  484.                 $addOp->execute($cmdargs[0]);
  485.                 $irc->yield(privmsg => $nick, "You have granted op to $cmdargs[0].");
  486.             } else {
  487.                 $irc->yield(privmsg => $nick, "You cannot do that.");
  488.             }
  489.         }
  490.         case 'deop' {
  491.             if ($isOp == 1) {
  492.                 my $index = &Find($cmdargs[0], @ops);
  493.                 delete $ops[$index];
  494.                 $removeOp->execute($cmdargs[0]);
  495.                 $irc->yield(privmsg => $nick, "You have removed op from $cmdargs[0].");
  496.             } else {
  497.                 $irc->yield(privmsg => $nick, "You cannot do that.");
  498.             }
  499.         }
  500.         case 'shutdown' {
  501.             if ($isOp == 1) {
  502.                 $irc->yield(quit => "Nevermore!");
  503.                 $irc->yield(shutdown => "");
  504.             } else {
  505.                 $irc->yield(privmsg => $nick, "You cannot do that.");
  506.             }
  507.         }
  508.         case 'help' {
  509.             for (my $i = 0; $i <= $#ophelp; $i++) {
  510.                 $irc->yield(privmsg => $nick, $ophelp[$i][0] . ' - ' . $ophelp[$i][1]);
  511.             }
  512.         }
  513.         else {
  514.             $irc->yield(privmsg => $nick, "I don't recognize that.");
  515.         }
  516.     }
  517.     return;
  518. }
  519.  
  520. sub on_join {
  521.  
  522.     return;
  523. }
  524.  
  525. sub on_mention {
  526.     state $lastCall = 0;
  527.     my ($kernel, $who, $where, $msg) = @_[KERNEL, ARG0, ARG1, ARG2];
  528.     my $nick = (split /!/, $who)[0];
  529.     my $channel = $where->[0];
  530.     my $ts = DateTime->now( time_zone => 'UTC' );
  531.     if ($ts->epoch >= ($lastCall + 60) && !($msg =~ /^[\.<]/)) {
  532.         $irc->yield(ctcp => $channel, "ACTION senses someone talking about it.");
  533.         $lastCall = $ts->epoch;
  534.     }
  535.     return;
  536. }
  537.  
  538. sub on_kick {
  539.     my ($kernel, $kicker, $where, $kickee, $why) = @_[KERNEL, ARG0, ARG1, ARG2, ARG3];
  540.     my $nick = (split /!/, $kickee)[0];
  541.     my $channel = $where;
  542.     $irc->yield(privmsg => $channel, "Meh...  I didn't like them anyway. :P");
  543.     return;
  544. }
  545.  
  546. sub irc_console_connect {
  547.     my ($peeradr, $peerport, $wheel_id) = @_[ARG0 .. ARG2];
  548.     return;
  549. }
  550.  
  551. sub irc_console_authed {
  552.     my $wheel_id = $_[ARG0];
  553.     return;
  554. }
  555.  
  556. sub irc_console_close {
  557.     my $wheel_id = $_[ARG0];
  558.     return;
  559. }
  560.  
  561. sub irc_console_rw_fail {
  562.     my ($peeradr, $peerport) = @_[ARG0, ARG1];
  563.     return;
  564. }
  565.  
  566. $poe_kernel->run();
  567. exit 0;
  568.  
  569. sub LoadOps {
  570.     my $results = $dbh->selectall_arrayref("SELECT name FROM Ops;", { Slice => {} });
  571.     my @ops;
  572.     foreach my $entry ( @$results ) {
  573.         push(@ops, $entry->{name});
  574.     }
  575.     if (!(@ops)) {
  576.         print "No ops defined.  Please enter name of first op: ";
  577.         my $firstop = <>;
  578.         chomp($firstop);
  579.         push(@ops, $firstop);
  580.     }
  581.     return @ops;
  582. }
  583.  
  584. sub LoadChannels {
  585.     my $results = $dbh->selectall_arrayref("SELECT name FROM Channels;", { Slice => {} });
  586.     my @channels;
  587.     foreach my $entry ( @$results ) {
  588.         push (@channels, $entry->{name});
  589.     }
  590.     return @channels;
  591. }
  592.  
  593. sub Find($@) {
  594.     my $s = shift;
  595.     $_ eq $s && return @_ while $_=pop;
  596.     -1;
  597. }
  598.  
  599. sub AddCommas {
  600.     local $_ = shift;
  601.     1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
  602.     return $_;
  603. }
  604.  
  605. sub ConvertFromSeconds {
  606.     my $seconds = $_[0];
  607.     my $days = "";
  608.     my $hours = "";
  609.     my $minutes = "";
  610.     if ($seconds > 86400) {
  611.         $days = int($seconds / 86400) . "d ";
  612.         $seconds = $seconds % 86400;
  613.     } else {
  614.         $days = "";
  615.     }
  616.     if ($seconds > 3600) {
  617.         $hours = int($seconds / 3600) . "h ";
  618.         $seconds = $seconds % 3600;
  619.     } else {
  620.         $hours = "";
  621.     }
  622.     if ($seconds > 60) {
  623.         $minutes = int($seconds / 60) . "m ";
  624.         $seconds = $seconds % 60;
  625.     } else {
  626.         $minutes = "";
  627.     }
  628.     if ($seconds > 0) {
  629.         $seconds .= "s"
  630.     } else {
  631.         $seconds = "0s";
  632.     }
  633.  
  634.     return ($days . $hours . $minutes . $seconds);
  635. }