2010-03-22 08:33:44 +01:00
# File: Factoids.pm
2010-03-24 07:47:40 +01:00
# Author: pragma_
2010-03-22 08:33:44 +01:00
#
# Purpose: Provides functionality for factoids and a type of external module execution.
package PBot::Factoids ;
use warnings ;
use strict ;
use HTML::Entities ;
use Time::HiRes qw( gettimeofday ) ;
use Carp ( ) ;
2013-10-12 15:35:57 +02:00
use POSIX qw( strftime ) ;
2015-06-26 07:56:10 +02:00
use Text::ParseWords ;
2010-03-22 08:33:44 +01:00
2014-05-17 22:08:19 +02:00
use PBot::PBot qw( $VERSION ) ;
2014-05-18 02:27:57 +02:00
use PBot::FactoidCommands ;
2010-03-22 08:33:44 +01:00
use PBot::FactoidModuleLauncher ;
2010-06-20 08:16:48 +02:00
use PBot::DualIndexHashObject ;
2010-03-22 08:33:44 +01:00
sub new {
if ( ref ( $ _ [ 1 ] ) eq 'HASH' ) {
Carp:: croak ( "Options to Factoids should be key/value pairs, not hash reference" ) ;
}
my ( $ class , % conf ) = @ _ ;
my $ self = bless { } , $ class ;
$ self - > initialize ( % conf ) ;
return $ self ;
}
sub initialize {
my ( $ self , % conf ) = @ _ ;
2014-05-18 02:27:57 +02:00
my $ filename = delete $ conf { filename } ;
2010-03-22 08:33:44 +01:00
my $ export_path = delete $ conf { export_path } ;
my $ export_site = delete $ conf { export_site } ;
2014-03-03 11:33:34 +01:00
my $ pbot = delete $ conf { pbot } // Carp:: croak ( "Missing pbot reference to Factoids" ) ;
2010-03-22 08:33:44 +01:00
2014-05-18 02:27:57 +02:00
$ self - > { factoids } = PBot::DualIndexHashObject - > new ( name = > 'Factoids' , filename = > $ filename ) ;
2010-03-22 08:33:44 +01:00
$ self - > { export_path } = $ export_path ;
$ self - > { export_site } = $ export_site ;
2014-05-18 02:27:57 +02:00
$ self - > { pbot } = $ pbot ;
$ self - > { commands } = PBot::FactoidCommands - > new ( pbot = > $ pbot ) ;
2010-03-22 08:33:44 +01:00
$ self - > { factoidmodulelauncher } = PBot::FactoidModuleLauncher - > new ( pbot = > $ pbot ) ;
2014-05-17 00:11:31 +02:00
2014-05-24 14:01:59 +02:00
$ self - > { pbot } - > { registry } - > add_default ( 'text' , 'factoids' , 'default_rate_limit' , '15' ) ;
2014-05-17 00:11:31 +02:00
$ self - > { pbot } - > { atexit } - > register ( sub { $ self - > save_factoids ; return ; } ) ;
2014-05-17 22:08:19 +02:00
$ self - > load_factoids ;
$ self - > add_factoid ( 'text' , '.*' , $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) , 'version' , "/say $VERSION" , 1 ) ;
2010-03-22 08:33:44 +01:00
}
sub load_factoids {
my $ self = shift ;
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "Loading factoids from " . $ self - > { factoids } - > filename . " ...\n" ) ;
2010-05-09 01:36:56 +02:00
2014-05-18 22:09:05 +02:00
$ self - > { factoids } - > load ;
2010-03-22 08:33:44 +01:00
2010-06-20 08:16:48 +02:00
my ( $ text , $ regex , $ modules ) ;
2010-05-09 01:36:56 +02:00
2014-05-18 22:09:05 +02:00
foreach my $ channel ( keys % { $ self - > { factoids } - > hash } ) {
foreach my $ trigger ( keys % { $ self - > { factoids } - > hash - > { $ channel } } ) {
$ text + + if $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { type } eq 'text' ;
$ regex + + if $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { type } eq 'regex' ;
$ modules + + if $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { type } eq 'module' ;
2010-03-22 08:33:44 +01:00
}
}
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( " " . ( $ text + $ regex + $ modules ) . " factoids loaded ($text text, $regex regexs, $modules modules).\n" ) ;
$ self - > { pbot } - > { logger } - > log ( "Done.\n" ) ;
2010-03-22 08:33:44 +01:00
}
sub save_factoids {
my $ self = shift ;
2014-05-18 22:09:05 +02:00
$ self - > { factoids } - > save ;
2010-06-20 08:16:48 +02:00
$ self - > export_factoids ;
}
2010-05-09 01:36:56 +02:00
2010-06-20 08:16:48 +02:00
sub add_factoid {
my $ self = shift ;
2014-05-17 22:08:19 +02:00
my ( $ type , $ channel , $ owner , $ trigger , $ action , $ dont_save ) = @ _ ;
2010-05-09 01:36:56 +02:00
2010-06-20 08:16:48 +02:00
$ type = lc $ type ;
2015-02-16 05:18:46 +01:00
$ channel = '.*' if $ channel !~ /^#/ ;
2010-06-20 08:16:48 +02:00
$ channel = lc $ channel ;
2010-05-09 01:36:56 +02:00
2014-05-18 22:09:05 +02:00
$ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { enabled } = 1 ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { type } = $ type ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { action } = $ action ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { owner } = $ owner ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { created_on } = gettimeofday ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { ref_count } = 0 ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { ref_user } = "nobody" ;
2014-05-24 14:01:59 +02:00
$ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { rate_limit } = $ self - > { pbot } - > { registry } - > get_value ( 'factoids' , 'default_rate_limit' ) ;
2010-03-23 19:24:02 +01:00
2014-05-17 22:08:19 +02:00
$ self - > save_factoids unless $ dont_save ;
2010-03-22 08:33:44 +01:00
}
2010-06-20 08:16:48 +02:00
sub remove_factoid {
2010-03-22 08:33:44 +01:00
my $ self = shift ;
2010-06-20 08:16:48 +02:00
my ( $ channel , $ trigger ) = @ _ ;
2010-03-22 08:33:44 +01:00
2015-02-16 05:18:46 +01:00
$ channel = '.*' if $ channel !~ /^#/ ;
2010-03-22 08:33:44 +01:00
$ channel = lc $ channel ;
2010-06-20 08:16:48 +02:00
2014-05-18 22:09:05 +02:00
delete $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } ;
2014-05-17 22:08:19 +02:00
2014-05-18 22:09:05 +02:00
if ( not scalar keys $ self - > { factoids } - > hash - > { $ channel } ) {
delete $ self - > { factoids } - > hash - > { $ channel } ;
2014-05-17 22:08:19 +02:00
}
2010-06-20 08:16:48 +02:00
$ self - > save_factoids ;
2010-03-22 08:33:44 +01:00
}
sub export_factoids {
my $ self = shift ;
my $ filename ;
if ( @ _ ) { $ filename = shift ; } else { $ filename = $ self - > export_path ; }
return if not defined $ filename ;
open FILE , "> $filename" or return "Could not open export path." ;
2010-06-20 08:16:48 +02:00
2010-03-22 08:33:44 +01:00
my $ time = localtime ;
2013-10-12 15:35:57 +02:00
print FILE "<html><head>\n<link href='css/blue.css' rel='stylesheet' type='text/css'>\n" ;
print FILE '<script type="text/javascript" src="js/jquery-latest.js"></script>' . "\n" ;
print FILE '<script type="text/javascript" src="js/jquery.tablesorter.js"></script>' . "\n" ;
2014-03-03 10:24:33 +01:00
print FILE '<script type="text/javascript" src="js/picnet.table.filter.min.js"></script>' . "\n" ;
2013-10-12 15:35:57 +02:00
print FILE "</head>\n<body><i>Last updated at $time</i>\n" ;
2013-10-14 19:22:06 +02:00
print FILE "<hr><h2>Candide's factoids</h2>\n" ;
2010-06-20 08:16:48 +02:00
2010-03-22 08:33:44 +01:00
my $ i = 0 ;
2013-10-12 15:35:57 +02:00
my $ table_id = 1 ;
2010-06-20 08:16:48 +02:00
2014-05-18 22:09:05 +02:00
foreach my $ channel ( sort keys % { $ self - > { factoids } - > hash } ) {
next if not scalar keys % { $ self - > { factoids } - > hash - > { $ channel } } ;
2013-10-12 15:52:12 +02:00
my $ chan = $ channel eq '.*' ? 'global' : $ channel ;
2013-10-13 12:23:49 +02:00
print FILE "<a href='#" . $ chan . "'>" . encode_entities ( $ chan ) . "</a><br>\n" ;
2013-10-12 15:52:12 +02:00
}
2014-05-18 22:09:05 +02:00
foreach my $ channel ( sort keys % { $ self - > { factoids } - > hash } ) {
next if not scalar keys % { $ self - > { factoids } - > hash - > { $ channel } } ;
2013-10-12 15:52:12 +02:00
my $ chan = $ channel eq '.*' ? 'global' : $ channel ;
print FILE "<a name='$chan'></a>\n" ;
2013-10-12 15:35:57 +02:00
print FILE "<hr>\n<h3>$chan</h3>\n<hr>\n" ;
print FILE "<table border=\"0\" id=\"table$table_id\" class=\"tablesorter\">\n" ;
print FILE "<thead>\n<tr>\n" ;
print FILE "<th>owner</th>\n" ;
print FILE "<th>created on</th>\n" ;
print FILE "<th>times referenced</th>\n" ;
print FILE "<th>factoid</th>\n" ;
print FILE "<th>last edited by</th>\n" ;
print FILE "<th>edited date</th>\n" ;
print FILE "<th>last referenced by</th>\n" ;
print FILE "<th>last referenced date</th>\n" ;
print FILE "</tr>\n</thead>\n<tbody>\n" ;
$ table_id + + ;
2014-05-18 22:09:05 +02:00
foreach my $ trigger ( sort keys % { $ self - > { factoids } - > hash - > { $ channel } } ) {
if ( $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { type } eq 'text' ) {
2010-06-20 08:16:48 +02:00
$ i + + ;
if ( $ i % 2 ) {
print FILE "<tr bgcolor=\"#dddddd\">\n" ;
} else {
print FILE "<tr>\n" ;
}
2010-07-01 03:43:49 +02:00
2014-05-18 22:09:05 +02:00
print FILE "<td>" . $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { owner } . "</td>\n" ;
print FILE "<td>" . encode_entities ( strftime "%Y/%m/%d %H:%M:%S" , localtime $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { created_on } ) . "</td>\n" ;
2013-10-12 15:35:57 +02:00
2014-05-18 22:09:05 +02:00
print FILE "<td>" . $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { ref_count } . "</td>\n" ;
2013-10-12 15:35:57 +02:00
2014-05-18 22:09:05 +02:00
my $ action = $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { action } ;
2010-07-01 07:54:04 +02:00
$ action =~ s/(.*?)http(s?:\/\/[^ ]+)/encode_entities($1) . "<a href='http" . encode_entities($2) . "'>http" . encode_entities($2) . "<\/a>"/ge ;
2010-07-01 03:43:49 +02:00
$ action =~ s/(.*)<\/a>(.*$)/"$1<\/a>" . encode_entities($2)/e ;
2014-04-07 06:50:00 +02:00
2014-05-18 22:09:05 +02:00
if ( exists $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { action_with_args } ) {
my $ with_args = $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { action_with_args } ;
2014-04-07 06:50:00 +02:00
$ with_args =~ s/(.*?)http(s?:\/\/[^ ]+)/encode_entities($1) . "<a href='http" . encode_entities($2) . "'>http" . encode_entities($2) . "<\/a>"/ge ;
$ with_args =~ s/(.*)<\/a>(.*$)/"$1<\/a>" . encode_entities($2)/e ;
print FILE "<td width=100%><b>$trigger</b> is $action<br><br><b>with_args:</b> $with_args</td>\n" ;
} else {
print FILE "<td width=100%><b>$trigger</b> is $action</td>\n" ;
}
2013-10-12 15:35:57 +02:00
2014-05-18 22:09:05 +02:00
if ( exists $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { edited_by } ) {
print FILE "<td>" . $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { edited_by } . "</td>\n" ;
print FILE "<td>" . encode_entities ( strftime "%Y/%m/%d %H:%M:%S" , localtime $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { edited_on } ) . "</td>\n" ;
2013-10-12 15:35:57 +02:00
} else {
print FILE "<td></td>\n" ;
print FILE "<td></td>\n" ;
}
2014-05-18 22:09:05 +02:00
print FILE "<td>" . $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { ref_user } . "</td>\n" ;
2013-10-12 15:35:57 +02:00
2014-05-18 22:09:05 +02:00
if ( exists $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { last_referenced_on } ) {
print FILE "<td>" . encode_entities ( strftime "%Y/%m/%d %H:%M:%S" , localtime $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { last_referenced_on } ) . "</td>\n" ;
2013-10-12 15:35:57 +02:00
} else {
print FILE "<td></td>\n" ;
}
print FILE "</tr>\n" ;
2010-03-22 08:33:44 +01:00
}
}
2013-10-12 15:35:57 +02:00
print FILE "</tbody>\n</table>\n" ;
2010-03-22 08:33:44 +01:00
}
2010-06-20 08:16:48 +02:00
2010-03-22 08:33:44 +01:00
print FILE "<hr>$i factoids memorized.<br>" ;
2010-06-29 08:12:52 +02:00
print FILE "<hr><i>Last updated at $time</i>\n" ;
2013-10-12 15:35:57 +02:00
print FILE "<script type='text/javascript'>\n" ;
$ table_id - - ;
print FILE '$(document).ready(function() {' . "\n" ;
while ( $ table_id > 0 ) {
print FILE '$("#table' . $ table_id . '").tablesorter();' . "\n" ;
2014-03-03 10:24:33 +01:00
print FILE '$("#table' . $ table_id . '").tableFilter();' . "\n" ;
2013-10-12 15:35:57 +02:00
$ table_id - - ;
}
print FILE "});\n" ;
print FILE "</script>\n" ;
print FILE "</body>\n</html>\n" ;
2010-06-20 08:16:48 +02:00
2010-03-22 08:33:44 +01:00
close ( FILE ) ;
2010-06-20 08:16:48 +02:00
2014-05-18 22:09:05 +02:00
#$self->{pbot}->{logger}->log("$i factoids exported to path: " . $self->export_path . ", site: " . $self->export_site . "\n");
2010-03-22 08:33:44 +01:00
return "$i factoids exported to " . $ self - > export_site ;
}
2010-04-02 19:33:18 +02:00
sub find_factoid {
2015-03-29 01:49:42 +01:00
my ( $ self , $ from , $ keyword , $ arguments , $ exact_channel , $ exact_trigger , $ find_alias ) = @ _ ;
2010-06-20 08:16:48 +02:00
2012-07-22 21:22:30 +02:00
my $ debug = 0 ;
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "find_factoid: from: [$from], kw: [$keyword], args: [" . ( defined $ arguments ? $ arguments : "undef" ) . "], " . ( defined $ exact_channel ? $ exact_channel : "undef" ) . ", " . ( defined $ exact_trigger ? $ exact_trigger : "undef" ) . "\n" ) if $ debug ;
2012-07-22 21:22:30 +02:00
2010-06-30 06:58:22 +02:00
$ from = '.*' if not defined $ from or $ from !~ /^#/ ;
2014-05-23 14:42:23 +02:00
$ from = lc $ from ;
2010-04-02 19:33:18 +02:00
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "from: $from\n" ) if $ debug ;
2012-07-22 21:22:30 +02:00
2014-10-14 04:30:14 +02:00
my $ string = $ keyword . ( defined $ arguments ? " $arguments" : "" ) ;
2010-04-02 19:33:18 +02:00
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "string: $string\n" ) if $ debug ;
2012-07-22 21:22:30 +02:00
2010-06-20 08:16:48 +02:00
my @ result = eval {
2015-07-22 00:07:56 +02:00
my @ results ;
2015-03-29 01:49:42 +01:00
for ( my $ depth = 0 ; $ depth < 5 ; $ depth + + ) {
2015-04-04 00:33:19 +02:00
if ( $ self - > { pbot } - > { commands } - > exists ( $ keyword ) ) {
return undef ;
}
2015-03-29 01:49:42 +01:00
# check factoids
2014-10-28 21:33:11 +01:00
foreach my $ channel ( sort keys % { $ self - > { factoids } - > hash } ) {
if ( $ exact_channel ) {
2015-04-10 23:59:17 +02:00
if ( $ exact_trigger ) {
next unless $ from eq lc $ channel ;
} else {
next unless $ from eq lc $ channel or $ channel eq '.*' ;
}
2014-10-28 21:33:11 +01:00
}
foreach my $ trigger ( keys % { $ self - > { factoids } - > hash - > { $ channel } } ) {
2015-03-29 01:49:42 +01:00
if ( $ keyword =~ m/^\Q$trigger\E$/i ) {
$ self - > { pbot } - > { logger } - > log ( "return $channel: $trigger\n" ) if $ debug ;
if ( $ find_alias && $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { action } =~ /^\/call\s+(.*)$/ ) {
my $ command ;
if ( length $ arguments ) {
$ command = "$1 $arguments" ;
} else {
$ command = $ 1 ;
}
( $ keyword , $ arguments ) = split / / , $ command , 2 ;
goto NEXT_DEPTH ;
2014-10-28 21:33:11 +01:00
}
2015-03-29 01:49:42 +01:00
2015-07-22 00:07:56 +02:00
if ( $ exact_channel ) {
return ( $ channel , $ trigger ) ;
} else {
push @ results , [ $ channel , $ trigger ] ;
}
2010-06-20 08:16:48 +02:00
}
2010-04-02 19:33:18 +02:00
}
}
2015-03-29 01:49:42 +01:00
# then check regex factoids
if ( not $ exact_trigger ) {
foreach my $ channel ( sort keys % { $ self - > { factoids } - > hash } ) {
if ( $ exact_channel ) {
next unless $ from eq lc $ channel or $ channel eq '.*' ;
}
2015-04-03 21:33:39 +02:00
foreach my $ trigger ( sort keys % { $ self - > { factoids } - > hash - > { $ channel } } ) {
2015-03-29 01:49:42 +01:00
if ( $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { type } eq 'regex' ) {
2015-07-22 00:07:56 +02:00
$ self - > { pbot } - > { logger } - > log ( "checking regex $string =~ m/$trigger/i\n" ) if $ debug >= 2 ;
2015-03-29 01:49:42 +01:00
if ( $ string =~ m/$trigger/i ) {
$ self - > { pbot } - > { logger } - > log ( "return regex $channel: $trigger\n" ) if $ debug ;
if ( $ find_alias ) {
my $ command = $ self - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { action } ;
( $ keyword , $ arguments ) = split / / , $ command , 2 ;
$ string = $ keyword . ( length $ arguments ? " $arguments" : "" ) ;
goto NEXT_DEPTH ;
}
2015-07-22 00:07:56 +02:00
if ( $ exact_channel ) {
return ( $ channel , $ trigger ) ;
} else {
push @ results , [ $ channel , $ trigger ] ;
}
2015-03-29 01:49:42 +01:00
}
}
}
}
}
NEXT_DEPTH:
last if not $ find_alias ;
2010-04-02 19:33:18 +02:00
}
2015-07-22 00:07:56 +02:00
if ( $ debug ) {
if ( not @ results ) {
$ self - > { pbot } - > { logger } - > log ( "find_factoid: no match\n" ) ;
} else {
$ self - > { pbot } - > { logger } - > log ( "find_factoid: got results: " . ( join ', ' , map { "$_->[0] -> $_->[1]" } @ results ) . "\n" ) ;
}
}
return @ results ;
2010-04-02 19:33:18 +02:00
} ;
if ( $@ ) {
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "find_factoid: bad regex: $@\n" ) ;
2010-04-02 19:33:18 +02:00
return undef ;
}
2010-06-20 08:16:48 +02:00
return @ result ;
2010-04-10 00:55:24 +02:00
}
2015-07-07 04:39:33 +02:00
sub expand_factoid_vars {
my ( $ self , $ from , $ action ) = @ _ ;
while ( $ action =~ /(?<!\\)\$([a-zA-Z0-9_\-]+)/g ) {
my $ v = $ 1 ;
2015-07-10 08:24:00 +02:00
next if $ v =~ m/^(nick|channel|randomnick)$/ ; # don't override special variables
2015-07-22 00:07:56 +02:00
my @ factoids = $ self - > find_factoid ( $ from , $ v , undef , 0 , 1 ) ;
next if not @ factoids ;
my ( $ var_chan , $ var ) = ( $ factoids [ 0 ] - > [ 0 ] , $ factoids [ 0 ] - > [ 1 ] ) ;
2015-07-07 04:39:33 +02:00
2015-07-24 02:46:33 +02:00
if ( $ self - > { factoids } - > hash - > { $ var_chan } - > { $ var } - > { type } eq 'text' ) {
2015-07-07 04:39:33 +02:00
my $ change = $ self - > { factoids } - > hash - > { $ var_chan } - > { $ var } - > { action } ;
my @ list = split ( /\s|(".*?")/ , $ change ) ;
my @ mylist ;
2015-07-24 02:46:33 +02:00
for ( my $ i = 0 ; $ i <= $# list ; $ i + + ) {
2015-07-07 04:39:33 +02:00
push @ mylist , $ list [ $ i ] if $ list [ $ i ] ;
}
my $ line = int ( rand ( $# mylist + 1 ) ) ;
$ mylist [ $ line ] =~ s/"//g ;
$ action =~ s/\$$var/$mylist[$line]/ ;
}
}
$ action =~ s/\\\$/\$/g ;
return $ action ;
}
2015-06-26 07:56:10 +02:00
sub expand_action_arguments {
my ( $ self , $ action , $ input , $ nick ) = @ _ ;
if ( not defined $ input or $ input eq '' ) {
$ action =~ s/\$args/$nick/g ;
} else {
$ action =~ s/\$args/$input/g ;
}
2015-09-08 10:30:08 +02:00
$ input =~ s/'/\\'/g if defined $ input ;
2015-07-02 00:21:08 +02:00
my @ args = shellwords ( $ input ) ;
2015-06-26 07:56:10 +02:00
while ( $ action =~ m/\$arg\[([^]]+)]/g ) {
my $ arg = $ 1 ;
if ( $ arg eq '*' ) {
if ( not defined $ input or $ input eq '' ) {
$ action =~ s/\$arg\[\*\]/$nick/ ;
} else {
$ action =~ s/\$arg\[\*\]/$input/ ;
}
next ;
}
if ( $ arg =~ m/([^:]*):(.*)/ ) {
my $ arg1 = $ 1 ;
my $ arg2 = $ 2 ;
my $ arg1i = $ arg1 ;
my $ arg2i = $ arg2 ;
$ arg1i = 0 if $ arg1i eq '' ;
$ arg2i = $# args if $ arg2i eq '' ;
$ arg2i = $# args if $ arg2i > $# args ;
my @ values = eval {
local $ SIG { __WARN__ } = sub { } ;
return @ args [ $ arg1i .. $ arg2i ] ;
} ;
if ( $@ ) {
next ;
} else {
my $ string = join ( ' ' , @ values ) ;
if ( $ string eq '' ) {
$ action =~ s/\s*\$arg\[$arg1:$arg2\]// ;
} else {
$ action =~ s/\$arg\[$arg1:$arg2\]/$string/ ;
}
}
next ;
}
my $ value = eval {
local $ SIG { __WARN__ } = sub { } ;
return $ args [ $ arg ] ;
} ;
if ( $@ ) {
next ;
} else {
if ( not defined $ value ) {
if ( $ arg == 0 ) {
$ action =~ s/\$arg\[$arg\]/$nick/ ;
} else {
$ action =~ s/\s*\$arg\[$arg\]// ;
}
} else {
$ action =~ s/\$arg\[$arg\]/$value/ ;
}
}
}
return $ action ;
}
2010-03-22 08:33:44 +01:00
sub interpreter {
my $ self = shift ;
2015-09-04 05:56:44 +02:00
my ( $ from , $ nick , $ user , $ host , $ depth , $ keyword , $ arguments , $ tonick , $ ref_from , $ referenced ) = @ _ ;
2010-06-20 08:16:48 +02:00
my ( $ result , $ channel ) ;
2010-03-22 08:33:44 +01:00
my $ pbot = $ self - > { pbot } ;
2015-09-04 05:56:44 +02:00
#$self->{pbot}->{logger}->log("enter factoid interpreter [$keyword][" . (defined $arguments ? $arguments : '') . "] referenced = $referenced\n");
2015-04-03 21:33:39 +02:00
return undef if not length $ keyword or $ depth > $ self - > { pbot } - > { registry } - > get_value ( 'interpreter' , 'max_recursion' ) ;
2010-06-30 13:36:45 +02:00
2011-01-29 02:21:17 +01:00
$ from = lc $ from ;
2014-10-14 04:30:14 +02:00
#$self->{pbot}->{logger}->log("factoids interpreter: kw: [$keyword] args: [$arguments] from: [$from], ref_from: [" . (defined $ref_from ? $ref_from : "undef") . "]\n");
2012-07-22 21:22:30 +02:00
2011-01-30 07:29:05 +01:00
# search for factoid against global channel and current channel (from unless ref_from is defined)
2010-06-20 08:16:48 +02:00
my $ original_keyword = $ keyword ;
2014-05-18 22:09:05 +02:00
#$self->{pbot}->{logger}->log("calling find_factoid in Factoids.pm, interpreter() to search for factoid against global/current\n");
2012-07-22 21:22:30 +02:00
( $ channel , $ keyword ) = $ self - > find_factoid ( $ ref_from ? $ ref_from : $ from , $ keyword , $ arguments , 1 ) ;
2011-01-30 07:29:05 +01:00
2012-07-22 21:22:30 +02:00
if ( not defined $ ref_from or $ ref_from eq '.*' ) {
2011-01-30 07:29:05 +01:00
$ ref_from = "" ;
} else {
2012-07-22 21:22:30 +02:00
$ ref_from = "[$ref_from] " ;
}
if ( defined $ channel and not $ channel eq '.*' and not lc $ channel eq $ from ) {
$ ref_from = "[$channel] " ;
2011-01-30 07:29:05 +01:00
}
$ arguments = "" if not defined $ arguments ;
2010-06-20 08:16:48 +02:00
2011-01-30 04:55:09 +01:00
# if no match found, attempt to call factoid from another channel if it exists there
2010-06-20 08:16:48 +02:00
if ( not defined $ keyword ) {
2014-10-14 04:30:14 +02:00
my $ string = "$original_keyword $arguments" ;
my $ lc_keyword = lc $ original_keyword ;
2011-01-29 02:21:17 +01:00
my $ comma = "" ;
my $ found = 0 ;
2014-10-14 04:30:14 +02:00
my $ chans = "" ;
2011-01-29 02:21:17 +01:00
my ( $ fwd_chan , $ fwd_trig ) ;
2011-01-30 04:55:09 +01:00
# build string of which channels contain the keyword, keeping track of the last one and count
2014-05-18 22:09:05 +02:00
foreach my $ chan ( keys % { $ self - > { factoids } - > hash } ) {
foreach my $ trig ( keys % { $ self - > { factoids } - > hash - > { $ chan } } ) {
2014-10-14 04:30:14 +02:00
my $ type = $ self - > { factoids } - > hash - > { $ chan } - > { $ trig } - > { type } ;
if ( ( $ type eq 'text' or $ type eq 'module' ) and lc $ trig eq $ lc_keyword ) {
2011-01-29 02:21:17 +01:00
$ chans . = $ comma . $ chan ;
$ comma = ", " ;
$ found + + ;
$ fwd_chan = $ chan ;
$ fwd_trig = $ trig ;
last ;
}
}
}
2011-01-30 04:55:09 +01:00
# if multiple channels have this keyword, then ask user to disambiguate
2011-01-29 02:21:17 +01:00
if ( $ found > 1 ) {
2015-09-04 05:56:44 +02:00
return undef if $ referenced ;
2013-02-25 03:27:24 +01:00
return $ ref_from . "Ambiguous keyword '$original_keyword' exists in multiple channels (use 'fact <channel> <keyword>' to choose one): $chans" ;
2011-01-29 02:21:17 +01:00
}
2011-01-30 04:55:09 +01:00
# if there's just one other channel that has this keyword, trigger that instance
2011-01-29 02:21:17 +01:00
elsif ( $ found == 1 ) {
2014-05-18 22:09:05 +02:00
$ pbot - > { logger } - > log ( "Found '$original_keyword' as '$fwd_trig' in [$fwd_chan]\n" ) ;
2015-09-04 05:56:44 +02:00
return $ pbot - > { factoids } - > interpreter ( $ from , $ nick , $ user , $ host , + + $ depth , $ fwd_trig , $ arguments , $ tonick , $ fwd_chan , $ referenced ) ;
2011-01-30 04:55:09 +01:00
}
# otherwise keyword hasn't been found, display similiar matches for all channels
else {
2011-01-30 03:44:56 +01:00
# if a non-nick argument was supplied, e.g., a sentence using the bot's nick, don't say anything
2015-09-04 05:56:44 +02:00
return undef if length $ arguments and not $ self - > { pbot } - > { nicklist } - > is_present ( $ from , $ arguments ) ;
2011-01-30 07:29:05 +01:00
2014-05-18 02:27:57 +02:00
my $ matches = $ self - > { commands } - > factfind ( $ from , $ nick , $ user , $ host , quotemeta $ original_keyword ) ;
2012-11-04 21:42:38 +01:00
# found factfind matches
if ( $ matches !~ m/^No factoids/ ) {
2015-09-04 05:56:44 +02:00
return undef if $ referenced ;
2013-02-25 03:27:24 +01:00
return "No such factoid '$original_keyword'; $matches" ;
2012-11-04 21:42:38 +01:00
}
# otherwise find levenshtein closest matches from all channels
2014-05-18 22:09:05 +02:00
$ matches = $ self - > { factoids } - > levenshtein_matches ( '.*' , lc $ original_keyword ) ;
2010-03-24 07:47:40 +01:00
2011-01-29 02:21:17 +01:00
# don't say anything if nothing similiar was found
return undef if $ matches eq 'none' ;
2015-09-04 05:56:44 +02:00
return undef if $ referenced ;
2010-06-20 08:16:48 +02:00
2011-01-29 02:21:17 +01:00
return $ ref_from . "No such factoid '$original_keyword'; did you mean $matches?" ;
}
2010-06-20 08:16:48 +02:00
}
2015-09-04 05:56:44 +02:00
return undef if $ referenced and $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { noembed } ;
2015-04-03 19:11:21 +02:00
if ( exists $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { last_referenced_on } ) {
if ( exists $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { last_referenced_in } ) {
if ( $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { last_referenced_in } eq $ from ) {
if ( gettimeofday - $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { last_referenced_on } < $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { rate_limit } ) {
2015-06-26 07:56:10 +02:00
return "/msg $nick $ref_from'$keyword' is rate-limited; try again in " . ( $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { rate_limit } - int ( gettimeofday - $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { last_referenced_on } ) ) . " seconds." unless $ self - > { pbot } - > { admins } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
2015-04-03 19:11:21 +02:00
}
}
}
}
2014-05-18 22:09:05 +02:00
my $ type = $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { type } ;
2010-03-24 07:47:40 +01:00
2015-04-03 19:11:21 +02:00
$ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { ref_count } + + ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { ref_user } = "$nick!$user\@$host" ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { last_referenced_on } = gettimeofday ;
$ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { last_referenced_in } = $ from || "stdin" ;
2010-06-20 08:16:48 +02:00
2015-04-03 19:11:21 +02:00
my $ action = $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { action } ;
2010-04-02 19:33:18 +02:00
2015-04-03 19:11:21 +02:00
if ( length $ arguments ) {
if ( exists $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { action_with_args } ) {
$ action = $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { action_with_args } ;
}
2010-04-02 19:33:18 +02:00
2015-06-26 07:56:10 +02:00
if ( $ action =~ m/\$args/ or $ action =~ m/\$arg\[/ ) {
$ action = $ self - > expand_action_arguments ( $ action , $ arguments , defined $ tonick ? $ tonick : $ nick ) ;
$ arguments = "" ;
} else {
2015-07-02 00:21:08 +02:00
if ( $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { type } eq 'text' ) {
my $ target = $ self - > { pbot } - > { nicklist } - > is_present ( $ from , $ arguments ) ;
2015-09-04 05:56:44 +02:00
if ( $ target and $ action !~ /\$nick/ ) {
2015-07-02 00:21:08 +02:00
if ( $ action !~ m/^(\/[^ ]+) / ) {
$ action =~ s/^/\/say $target: $keyword is / unless defined $ tonick ;
} else {
if ( $ 1 eq '/say' ) {
$ action =~ s/^\/say /\/say $target: / ;
} elsif ( $ 1 ne '/call' ) {
$ action =~ s/$/ ($target)/ ;
}
}
2015-06-26 07:56:10 +02:00
} else {
2015-07-02 00:21:08 +02:00
if ( $ action !~ m/^\/.+ / ) {
$ action =~ s/^/\/say $keyword is / unless defined $ tonick ;
}
2015-04-03 19:11:21 +02:00
}
2010-08-14 11:45:58 +02:00
}
}
2015-04-03 19:11:21 +02:00
} else {
# no arguments supplied
if ( defined $ tonick ) {
2015-06-26 07:56:10 +02:00
$ action = $ self - > expand_action_arguments ( $ action , undef , $ tonick ) ;
2015-04-03 19:11:21 +02:00
} else {
2015-06-26 07:56:10 +02:00
$ action = $ self - > expand_action_arguments ( $ action , undef , $ nick ) ;
2010-06-21 17:23:46 +02:00
}
}
2015-04-15 05:14:22 +02:00
# Check if it's an alias
if ( $ action =~ /^\/call\s+(.*)$/ ) {
2015-07-07 04:39:33 +02:00
my $ command = $ self - > expand_factoid_vars ( $ from , $ 1 ) ;
2015-04-15 05:14:22 +02:00
if ( length $ arguments ) {
2015-07-07 05:01:17 +02:00
$ command . = " $arguments" ;
2015-04-15 05:14:22 +02:00
}
$ pbot - > { logger } - > log ( "[" . ( defined $ from ? $ from : "stdin" ) . "] ($nick!$user\@$host) [$keyword] aliased to: [$command]\n" ) ;
2015-09-04 05:56:44 +02:00
return $ pbot - > { interpreter } - > interpret ( $ from , $ nick , $ user , $ host , $ depth , $ command , $ tonick , $ referenced ) ;
2015-04-15 05:14:22 +02:00
}
2015-04-03 19:11:21 +02:00
if ( defined $ tonick ) { # !tell foo about bar
$ self - > { pbot } - > { logger } - > log ( "($from): $nick!$user\@$host) sent to $tonick\n" ) ;
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
2010-04-02 19:33:18 +02:00
2015-04-03 19:11:21 +02:00
# get rid of original caller's nick
$ action =~ s/^\/([^ ]+) \Q$nick\E:\s+/\/$1 / ;
$ action =~ s/^\Q$nick\E:\s+// ;
2010-04-02 19:33:18 +02:00
2015-04-03 19:11:21 +02:00
if ( $ action =~ s/^\/say\s+//i || $ action =~ s/^\/me\s+/* $botnick /i
|| $ action =~ /^\/msg\s+/i ) {
$ action = "/say $tonick: $action" ;
} else {
$ action = "/say $tonick: $keyword is $action" ;
}
2014-03-18 15:55:34 +01:00
2015-04-03 19:11:21 +02:00
$ self - > { pbot } - > { logger } - > log ( "result set to [$action]\n" ) ;
2010-04-02 19:33:18 +02:00
}
2015-06-21 02:48:00 +02:00
$ self - > { pbot } - > { logger } - > log ( "(" . ( defined $ from ? $ from : "(undef)" ) . "): $nick!$user\@$host: $keyword: action: \"$action\"\n" ) ;
2010-04-02 19:33:18 +02:00
2015-09-04 05:56:44 +02:00
if ( $ referenced ) {
return undef if $ action =~ m/\$(?:nick|arg)/ ;
return undef if $ arguments =~ m/\$(?:nick|arg)/ ;
}
2015-04-03 19:11:21 +02:00
$ action =~ s/\$nick/$nick/g ;
$ action =~ s/\$channel/$from/g ;
2015-06-08 13:43:00 +02:00
$ action =~ s/\$randomnick/my $random = $self->{pbot}->{nicklist}->random_nick($from); $random ? $random : $nick/ge ;
2010-04-02 19:33:18 +02:00
2015-07-07 04:39:33 +02:00
$ action = $ self - > expand_factoid_vars ( $ from , $ action ) ;
2012-08-24 00:50:07 +02:00
2015-04-03 19:11:21 +02:00
if ( $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { enabled } == 0 ) {
$ self - > { pbot } - > { logger } - > log ( "$keyword disabled.\n" ) ;
return "/msg $nick $ref_from$keyword is currently disabled." ;
}
2010-03-22 08:33:44 +01:00
2015-04-03 19:11:21 +02:00
if ( $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { type } eq 'module' ) {
my $ preserve_whitespace = $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { preserve_whitespace } ;
$ preserve_whitespace = 0 if not defined $ preserve_whitespace ;
2010-04-02 19:33:18 +02:00
2015-09-04 05:56:44 +02:00
return $ ref_from . $ self - > { factoidmodulelauncher } - > execute_module ( $ from , $ tonick , $ nick , $ user , $ host , "$keyword $arguments" , $ keyword , $ arguments , $ preserve_whitespace , $ referenced ) ;
2015-04-03 19:11:21 +02:00
}
elsif ( $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { type } eq 'text' ) {
$ self - > { pbot } - > { logger } - > log ( "Found factoid\n" ) ;
2010-06-20 08:16:48 +02:00
2015-04-03 19:11:21 +02:00
# Don't allow user-custom /msg factoids, unless factoid triggered by admin
if ( ( $ action =~ m/^\/msg/i ) and ( not $ self - > { pbot } - > { admins } - > loggedin ( $ from , "$nick!$user\@$host" ) ) ) {
$ self - > { pbot } - > { logger } - > log ( "[ABUSE] Bad factoid (contains /msg): $action\n" ) ;
return "You must login to use this command."
2010-03-22 08:33:44 +01:00
}
2010-04-02 19:33:18 +02:00
2013-10-12 17:06:27 +02:00
if ( $ ref_from ) {
2015-04-03 19:11:21 +02:00
if ( $ action =~ s/^\/say\s+/$ref_from/i || $ action =~ s/^\/me\s+(.*)/\/me $1 $ref_from/i
|| $ action =~ s/^\/msg\s+([^ ]+)/\/msg $1 $ref_from/i ) {
return $ action ;
2013-10-12 17:06:27 +02:00
} else {
2015-04-03 19:11:21 +02:00
return $ ref_from . "$keyword is $action" ;
2013-10-12 17:06:27 +02:00
}
2013-10-14 19:22:06 +02:00
} else {
2015-04-03 19:11:21 +02:00
if ( $ action =~ m/^\/say/i || $ action =~ m/^\/me/i || $ action =~ m/^\/msg/i ) {
return $ action ;
2013-10-14 19:22:06 +02:00
} else {
2015-04-03 19:11:21 +02:00
return "$keyword is $action" ;
2013-10-14 19:22:06 +02:00
}
2010-04-02 19:33:18 +02:00
}
2014-05-18 22:09:05 +02:00
} elsif ( $ self - > { factoids } - > hash - > { $ channel } - > { $ keyword } - > { type } eq 'regex' ) {
2010-04-02 19:33:18 +02:00
$ result = eval {
2010-06-30 05:48:13 +02:00
my $ string = "$original_keyword" . ( defined $ arguments ? " $arguments" : "" ) ;
my $ cmd ;
2010-04-02 19:33:18 +02:00
if ( $ string =~ m/$keyword/i ) {
2015-04-03 19:11:21 +02:00
$ self - > { pbot } - > { logger } - > log ( "[$string] matches [$keyword] - calling [" . $ action . "$']\n" ) ;
$ cmd = $ action . $' ;
2010-06-20 08:16:48 +02:00
my ( $ a , $ b , $ c , $ d , $ e , $ f , $ g , $ h , $ i , $ before , $ after ) = ( $ 1 , $ 2 , $ 3 , $ 4 , $ 5 , $ 6 , $ 7 , $ 8 , $ 9 , $` , $' ) ;
2010-04-02 19:33:18 +02:00
$ cmd =~ s/\$1/$a/g ;
$ cmd =~ s/\$2/$b/g ;
$ cmd =~ s/\$3/$c/g ;
$ cmd =~ s/\$4/$d/g ;
$ cmd =~ s/\$5/$e/g ;
$ cmd =~ s/\$6/$f/g ;
$ cmd =~ s/\$7/$g/g ;
$ cmd =~ s/\$8/$h/g ;
$ cmd =~ s/\$9/$i/g ;
$ cmd =~ s/\$`/$before/g ;
$ cmd =~ s/\$'/$after/g ;
$ cmd =~ s/^\s+// ;
$ cmd =~ s/\s+$// ;
2010-06-30 05:48:13 +02:00
} else {
2015-04-03 19:11:21 +02:00
$ cmd = $ action ;
2010-04-02 19:33:18 +02:00
}
2010-06-30 05:48:13 +02:00
2015-09-04 05:56:44 +02:00
$ result = $ pbot - > { interpreter } - > interpret ( $ from , $ nick , $ user , $ host , $ depth , $ cmd , $ tonick , $ referenced ) ;
2011-01-30 08:18:28 +01:00
return $ result ;
2010-04-02 19:33:18 +02:00
} ;
2010-06-30 05:48:13 +02:00
2010-04-02 19:33:18 +02:00
if ( $@ ) {
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "Regex fail: $@\n" ) ;
2014-10-14 04:30:14 +02:00
return "" ;
2010-04-02 19:33:18 +02:00
}
2011-01-29 02:21:17 +01:00
return $ ref_from . $ result ;
2010-04-02 19:33:18 +02:00
} else {
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "($from): $nick!$user\@$host): Unknown command type for '$keyword'\n" ) ;
2011-01-29 02:21:17 +01:00
return "/me blinks." . " $ref_from" ;
2010-03-22 08:33:44 +01:00
}
}
sub export_path {
my $ self = shift ;
if ( @ _ ) { $ self - > { export_path } = shift ; }
return $ self - > { export_path } ;
}
sub logger {
my $ self = shift ;
if ( @ _ ) { $ self - > { logger } = shift ; }
return $ self - > { logger } ;
}
sub export_site {
my $ self = shift ;
if ( @ _ ) { $ self - > { export_site } = shift ; }
return $ self - > { export_site } ;
}
sub factoids {
my $ self = shift ;
return $ self - > { factoids } ;
}
sub filename {
my $ self = shift ;
if ( @ _ ) { $ self - > { filename } = shift ; }
return $ self - > { filename } ;
}
1 ;