2010-03-22 08:33:44 +01:00
# File: FactoidCommands.pm
2010-03-24 07:47:40 +01:00
# Author: pragma_
2010-03-22 08:33:44 +01:00
#
# Purpose: Administrative command subroutines.
2017-03-05 22:33:31 +01:00
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
2010-03-22 08:33:44 +01:00
package PBot::FactoidCommands ;
use warnings ;
use strict ;
use Carp ( ) ;
2010-06-21 12:44:15 +02:00
use Time::Duration ;
use Time::HiRes qw( gettimeofday ) ;
2015-12-15 01:13:43 +01:00
use Getopt::Long qw( GetOptionsFromString ) ;
use POSIX qw( strftime ) ;
2017-08-26 16:03:01 +02:00
use Storable ;
2019-07-01 02:45:05 +02:00
use LWP::UserAgent ( ) ;
2010-03-22 08:33:44 +01:00
2017-09-05 04:07:10 +02:00
use PBot::Utils::SafeFilename ;
2010-03-22 08:33:44 +01:00
sub new {
2019-05-28 18:19:42 +02:00
if ( ref ( $ _ [ 1 ] ) eq 'HASH' ) {
2010-03-22 08:33:44 +01:00
Carp:: croak ( "Options to FactoidCommands should be key/value pairs, not hash reference" ) ;
}
my ( $ class , % conf ) = @ _ ;
my $ self = bless { } , $ class ;
$ self - > initialize ( % conf ) ;
return $ self ;
}
2017-08-24 04:25:43 +02:00
our % factoid_metadata_levels = (
2015-07-13 11:47:30 +02:00
created_on = > 90 ,
2011-01-25 23:40:22 +01:00
enabled = > 10 ,
2015-07-13 11:47:30 +02:00
last_referenced_in = > 90 ,
last_referenced_on = > 90 ,
modulelauncher_subpattern = > 90 ,
owner = > 90 ,
2011-01-25 23:40:22 +01:00
rate_limit = > 10 ,
2015-07-13 11:47:30 +02:00
ref_count = > 90 ,
ref_user = > 90 ,
type = > 90 ,
edited_by = > 90 ,
edited_on = > 90 ,
2013-09-13 23:48:19 +02:00
locked = > 10 ,
2014-05-31 03:04:26 +02:00
add_nick = > 10 ,
2014-07-11 14:56:17 +02:00
nooverride = > 10 ,
2019-05-13 09:07:08 +02:00
'effective-level' = > 40 ,
2017-08-24 04:25:43 +02:00
'persist-key' = > 20 ,
2017-08-31 12:10:20 +02:00
'interpolate' = > 10 ,
2011-01-25 23:40:22 +01:00
# all others are allowed to be factset by anybody/default to level 0
) ;
2010-03-22 08:33:44 +01:00
sub initialize {
my ( $ self , % conf ) = @ _ ;
my $ pbot = delete $ conf { pbot } ;
2019-05-28 18:19:42 +02:00
if ( not defined $ pbot ) {
2010-03-22 08:33:44 +01:00
Carp:: croak ( "Missing pbot reference to FactoidCommands" ) ;
}
$ self - > { pbot } = $ pbot ;
2014-12-30 08:15:46 +01:00
$ pbot - > { registry } - > add_default ( 'text' , 'general' , 'module_repo' , $ conf { module_repo } // 'https://github.com/pragma-/pbot/blob/master/modules/' ) ;
2019-06-26 18:34:19 +02:00
2014-05-18 22:09:05 +02:00
$ pbot - > { commands } - > register ( sub { return $ self - > factadd ( @ _ ) } , "learn" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factadd ( @ _ ) } , "factadd" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factrem ( @ _ ) } , "forget" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factrem ( @ _ ) } , "factrem" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factshow ( @ _ ) } , "factshow" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factinfo ( @ _ ) } , "factinfo" , 0 ) ;
2015-12-13 23:24:25 +01:00
$ pbot - > { commands } - > register ( sub { return $ self - > factlog ( @ _ ) } , "factlog" , 0 ) ;
2017-08-26 16:03:01 +02:00
$ pbot - > { commands } - > register ( sub { return $ self - > factundo ( @ _ ) } , "factundo" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factredo ( @ _ ) } , "factredo" , 0 ) ;
2014-05-18 22:09:05 +02:00
$ pbot - > { commands } - > register ( sub { return $ self - > factset ( @ _ ) } , "factset" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factunset ( @ _ ) } , "factunset" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factchange ( @ _ ) } , "factchange" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factalias ( @ _ ) } , "factalias" , 0 ) ;
2014-05-23 14:42:23 +02:00
$ pbot - > { commands } - > register ( sub { return $ self - > factmove ( @ _ ) } , "factmove" , 0 ) ;
2014-05-18 22:09:05 +02:00
$ pbot - > { commands } - > register ( sub { return $ self - > call_factoid ( @ _ ) } , "fact" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > factfind ( @ _ ) } , "factfind" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > list ( @ _ ) } , "list" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > top20 ( @ _ ) } , "top20" , 0 ) ;
2015-07-13 11:47:30 +02:00
$ pbot - > { commands } - > register ( sub { return $ self - > load_module ( @ _ ) } , "load" , 90 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > unload_module ( @ _ ) } , "unload" , 90 ) ;
2014-05-24 14:01:59 +02:00
$ pbot - > { commands } - > register ( sub { return $ self - > histogram ( @ _ ) } , "histogram" , 0 ) ;
$ pbot - > { commands } - > register ( sub { return $ self - > count ( @ _ ) } , "count" , 0 ) ;
2010-06-23 04:15:13 +02:00
2010-06-27 04:52:06 +02:00
# the following commands have not yet been updated to use the new factoid structure
# DO NOT USE!! Factoid corruption may occur.
2014-05-18 22:09:05 +02:00
$ pbot - > { commands } - > register ( sub { return $ self - > add_regex ( @ _ ) } , "regex" , 999 ) ;
2010-06-21 15:54:27 +02:00
}
sub call_factoid {
my $ self = shift ;
2017-11-23 23:11:54 +01:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2018-08-09 02:38:57 +02:00
my ( $ chan , $ keyword , $ args ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 3 ) ;
2010-06-21 15:54:27 +02:00
2019-05-28 18:19:42 +02:00
if ( not defined $ chan or not defined $ keyword ) {
2014-05-17 22:08:19 +02:00
return "Usage: fact <channel> <keyword> [arguments]" ;
2010-06-21 15:54:27 +02:00
}
2019-06-10 01:33:27 +02:00
my ( $ channel , $ trigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ chan , $ keyword , arguments = > $ args , exact_channel = > 1 , exact_trigger = > 1 ) ;
2010-06-21 15:54:27 +02:00
2019-05-28 18:19:42 +02:00
if ( not defined $ trigger ) {
2010-06-21 15:54:27 +02:00
return "No such factoid '$keyword' exists for channel '$chan'" ;
}
2017-11-23 23:11:54 +01:00
$ stuff - > { keyword } = $ trigger ;
2017-12-01 01:41:50 +01:00
$ stuff - > { trigger } = $ trigger ;
$ stuff - > { ref_from } = $ channel ;
2017-11-23 23:11:54 +01:00
$ stuff - > { arguments } = $ args ;
2017-12-01 01:41:50 +01:00
$ stuff - > { root_keyword } = $ trigger ;
2017-11-23 23:11:54 +01:00
return $ self - > { pbot } - > { factoids } - > interpreter ( $ stuff ) ;
2010-06-20 08:16:48 +02:00
}
2015-12-13 22:58:01 +01:00
sub log_factoid {
my $ self = shift ;
2017-08-26 16:03:01 +02:00
my ( $ channel , $ trigger , $ hostmask , $ msg , $ dont_save_undo ) = @ _ ;
2015-12-13 22:58:01 +01:00
2017-08-27 06:35:46 +02:00
my $ channel_path = $ channel ;
$ channel_path = 'global' if $ channel_path eq '.*' ;
2015-12-13 22:58:01 +01:00
2017-09-05 04:07:10 +02:00
my $ channel_path_safe = safe_filename $ channel_path ;
my $ trigger_safe = safe_filename $ trigger ;
2015-12-13 22:58:01 +01:00
my $ path = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/factlog' ;
2017-09-05 04:07:10 +02:00
open my $ fh , ">> $path/$trigger_safe.$channel_path_safe" or do {
2015-12-13 22:58:01 +01:00
$ self - > { pbot } - > { logger } - > log ( "Failed to open factlog for $channel/$trigger: $!\n" ) ;
return ;
2015-12-14 23:30:44 +01:00
} ;
2015-12-13 22:58:01 +01:00
my $ now = gettimeofday ;
print $ fh "$now $hostmask $msg\n" ;
close $ fh ;
2017-08-26 16:03:01 +02:00
return if $ dont_save_undo ;
2017-09-05 04:07:10 +02:00
my $ undos = eval { retrieve ( "$path/$trigger_safe.$channel_path_safe.undo" ) ; } ;
2017-08-26 16:03:01 +02:00
if ( not $ undos ) {
$ undos = {
idx = > - 1 ,
list = > []
} ;
}
# TODO: use registry instead of hardcoded 20... meh
if ( @ { $ undos - > { list } } > 20 ) {
shift @ { $ undos - > { list } } ;
$ undos - > { idx } - - ;
}
if ( $ undos - > { idx } > - 1 and @ { $ undos - > { list } } > $ undos - > { idx } + 1 ) {
splice @ { $ undos - > { list } } , $ undos - > { idx } + 1 ;
}
push @ { $ undos - > { list } } , $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } ;
$ undos - > { idx } + + ;
2017-09-05 04:07:10 +02:00
eval { store $ undos , "$path/$trigger_safe.$channel_path_safe.undo" ; } ;
$ self - > { pbot } - > { logger } - > log ( "Error storing undo: $@\n" ) if $@ ;
2017-08-26 16:03:01 +02:00
}
sub find_factoid_with_optional_channel {
2019-06-10 01:33:27 +02:00
my ( $ self , $ from , $ arguments , $ command , % opts ) = @ _ ;
2019-06-26 18:34:19 +02:00
2019-06-10 18:21:35 +02:00
my % default_opts = (
usage = > undef ,
explicit = > 0 ,
exact_channel = > 0
) ;
% opts = ( % default_opts , % opts ) ;
2018-08-09 02:38:57 +02:00
my $ arglist = $ self - > { pbot } - > { interpreter } - > make_args ( $ arguments ) ;
my ( $ from_chan , $ from_trigger , $ remaining_args ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 3 ) ;
2017-08-26 16:03:01 +02:00
2017-08-28 03:52:14 +02:00
if ( not defined $ from_chan or ( not defined $ from_chan and not defined $ from_trigger ) ) {
2019-06-10 01:33:27 +02:00
return "Usage: $command [channel] <keyword>" if not $ opts { usage } ;
return $ opts { usage } ;
2017-08-26 16:03:01 +02:00
}
my $ needs_disambig ;
if ( not defined $ from_trigger ) {
2017-08-28 03:52:14 +02:00
# cmd arg1, so insert $from as channel
2017-08-26 16:03:01 +02:00
$ from_trigger = $ from_chan ;
$ from_chan = $ from ;
2017-08-27 09:56:55 +02:00
$ remaining_args = "" ;
2017-08-26 16:03:01 +02:00
#$needs_disambig = 1;
2017-08-28 03:52:14 +02:00
} else {
# cmd arg1 arg2 [...?]
if ( $ from_chan !~ /^#/ and lc $ from_chan ne 'global' and $ from_chan ne '.*' ) {
# not a channel or global, so must be a keyword
my $ keyword = $ from_chan ;
$ from_chan = $ from ;
$ from_trigger = $ keyword ;
2018-08-09 02:38:57 +02:00
( undef , $ remaining_args ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 2 ) ;
2017-08-28 03:52:14 +02:00
}
2017-08-26 16:03:01 +02:00
}
$ from_chan = '.*' if $ from_chan !~ /^#/ ;
$ from_chan = lc $ from_chan ;
2017-12-11 23:24:29 +01:00
my ( $ channel , $ trigger ) ;
2017-08-26 16:03:01 +02:00
2019-06-10 01:33:27 +02:00
if ( $ opts { exact_channel } == 1 ) {
( $ channel , $ trigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ from_chan , $ from_trigger , exact_channel = > 1 , exact_trigger = > 1 ) ;
2017-12-11 23:24:29 +01:00
if ( not defined $ channel ) {
$ from_chan = 'the global channel' if $ from_chan eq '.*' ;
return "/say $from_trigger not found in $from_chan." ;
2017-08-26 16:03:01 +02:00
}
2017-12-11 23:24:29 +01:00
} else {
2019-06-10 01:33:27 +02:00
my @ factoids = $ self - > { pbot } - > { factoids } - > find_factoid ( $ from_chan , $ from_trigger , exact_trigger = > 1 ) ;
2017-08-26 16:03:01 +02:00
2017-12-11 23:24:29 +01:00
if ( not @ factoids or not $ factoids [ 0 ] ) {
if ( $ needs_disambig ) {
return "/say $from_trigger not found" ;
} else {
$ from_chan = 'global channel' if $ from_chan eq '.*' ;
return "/say $from_trigger not found in $from_chan" ;
}
}
2017-08-26 16:03:01 +02:00
2017-12-11 23:24:29 +01:00
if ( @ factoids > 1 ) {
if ( $ needs_disambig or not grep { $ _ - > [ 0 ] eq $ from_chan } @ factoids ) {
2019-06-10 01:33:27 +02:00
unless ( $ opts { explicit } ) {
2017-12-11 23:24:29 +01:00
foreach my $ factoid ( @ factoids ) {
if ( $ factoid - > [ 0 ] eq '.*' ) {
( $ channel , $ trigger ) = ( $ factoid - > [ 0 ] , $ factoid - > [ 1 ] ) ;
}
}
}
if ( not defined $ channel ) {
return "/say $from_trigger found in multiple channels: " . ( join ', ' , sort map { $ _ - > [ 0 ] eq '.*' ? 'global' : $ _ - > [ 0 ] } @ factoids ) . "; use `$command <channel> $from_trigger` to disambiguate." ;
}
} else {
2017-08-31 07:54:38 +02:00
foreach my $ factoid ( @ factoids ) {
2017-12-11 23:24:29 +01:00
if ( $ factoid - > [ 0 ] eq $ from_chan ) {
2017-08-31 07:54:38 +02:00
( $ channel , $ trigger ) = ( $ factoid - > [ 0 ] , $ factoid - > [ 1 ] ) ;
2017-12-11 23:24:29 +01:00
last ;
2017-08-31 07:54:38 +02:00
}
}
}
2017-08-26 16:03:01 +02:00
} else {
2017-12-11 23:24:29 +01:00
( $ channel , $ trigger ) = ( $ factoids [ 0 ] - > [ 0 ] , $ factoids [ 0 ] - > [ 1 ] ) ;
2017-08-26 16:03:01 +02:00
}
}
$ channel = '.*' if $ channel eq 'global' ;
$ from_chan = '.*' if $ channel eq 'global' ;
2019-06-10 01:33:27 +02:00
if ( $ opts { explicit } and $ channel =~ /^#/ and $ from_chan =~ /^#/ and $ channel ne $ from_chan ) {
2017-09-02 09:14:13 +02:00
return "/say $trigger belongs to $channel, not $from_chan. Please switch to or explicitly specify $channel." ;
2017-08-26 16:03:01 +02:00
}
2017-08-27 09:56:55 +02:00
return ( $ channel , $ trigger , $ remaining_args ) ;
2017-08-26 16:03:01 +02:00
}
2017-08-29 08:14:32 +02:00
sub hash_differences_as_string {
my ( $ self , $ old , $ new ) = @ _ ;
my @ exclude = qw/created_on last_referenced_in last_referenced_on ref_count ref_user edited_by edited_on/ ;
my % diff ;
foreach my $ key ( keys %$ new ) {
next if grep { $ key eq $ _ } @ exclude ;
if ( not exists $ old - > { $ key } or $ old - > { $ key } ne $ new - > { $ key } ) {
$ diff { $ key } = $ new - > { $ key } ;
}
}
2019-06-03 01:21:42 +02:00
foreach my $ key ( keys %$ old ) {
next if grep { $ key eq $ _ } @ exclude ;
if ( not exists $ new - > { $ key } ) {
$ diff { "deleted $key" } = undef ;
}
}
2017-08-29 08:14:32 +02:00
if ( not keys % diff ) {
return "No change." ;
}
my $ changes = "" ;
my $ comma = "" ;
foreach my $ key ( sort keys % diff ) {
2019-06-03 01:21:42 +02:00
if ( defined $ diff { $ key } ) {
$ changes . = "$comma$key => $diff{$key}" ;
} else {
$ changes . = "$comma$key" ;
}
2017-08-29 08:14:32 +02:00
$ comma = ", " ;
}
return $ changes
}
2017-12-12 12:38:45 +01:00
sub list_undo_history {
2017-12-13 00:32:50 +01:00
my ( $ self , $ undos , $ start_from ) = @ _ ;
$ start_from - - if defined $ start_from ;
$ start_from = 0 if not defined $ start_from or $ start_from < 0 ;
2017-12-12 12:38:45 +01:00
my $ result = "" ;
2017-12-13 00:32:50 +01:00
if ( $ start_from > @ { $ undos - > { list } } ) {
if ( @ { $ undos - > { list } } == 1 ) {
return "But there is only one revision available." ;
} else {
return "But there are only " . @ { $ undos - > { list } } . " revisions available." ;
}
}
if ( $ start_from == 0 ) {
if ( $ undos - > { idx } == 0 ) {
$ result . = "*1*: " ;
} else {
$ result . = "1: " ;
}
$ result . = $ self - > hash_differences_as_string ( { } , $ undos - > { list } - > [ 0 ] ) . ";\n\n" ;
$ start_from + + ;
2017-12-12 12:38:45 +01:00
}
2017-12-13 00:32:50 +01:00
for ( my $ i = $ start_from ; $ i < @ { $ undos - > { list } } ; $ i + + ) {
2017-12-12 12:38:45 +01:00
if ( $ i == $ undos - > { idx } ) {
$ result . = "*" . ( $ i + 1 ) . "*: " ;
} else {
$ result . = ( $ i + 1 ) . ": " ;
}
$ result . = $ self - > hash_differences_as_string ( $ undos - > { list } - > [ $ i - 1 ] , $ undos - > { list } - > [ $ i ] ) ;
2017-12-13 00:32:50 +01:00
$ result . = ";\n\n" ;
2017-12-12 12:38:45 +01:00
}
return $ result ;
}
2017-08-26 16:03:01 +02:00
sub factundo {
2018-08-09 02:38:57 +02:00
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2017-08-26 16:03:01 +02:00
2017-12-13 00:32:50 +01:00
my $ usage = "Usage: factundo [-l [N]] [-r N] [channel] <keyword> (-l list undo history, optionally starting from N; -r jump to revision N)" ;
2017-12-12 12:38:45 +01:00
my $ getopt_error ;
local $ SIG { __WARN__ } = sub {
$ getopt_error = shift ;
chomp $ getopt_error ;
} ;
my ( $ list_undos , $ goto_revision ) ;
my ( $ ret , $ args ) = GetOptionsFromString ( $ arguments ,
2017-12-13 00:32:50 +01:00
'l:i' = > \ $ list_undos ,
2017-12-12 12:38:45 +01:00
'r=i' = > \ $ goto_revision ) ;
return "/say $getopt_error -- $usage" if defined $ getopt_error ;
return $ usage if @$ args > 2 ;
return $ usage if not @$ args ;
2019-06-23 03:30:40 +02:00
$ arguments = join ( ' ' , map { $ _ = "'$_'" if $ _ =~ m/ / ; $ _ ; } @$ args ) ;
2018-08-09 02:38:57 +02:00
my $ arglist = $ self - > { pbot } - > { interpreter } - > make_args ( $ arguments ) ;
2017-12-12 12:38:45 +01:00
2019-06-10 01:33:27 +02:00
my ( $ channel , $ trigger ) = $ self - > find_factoid_with_optional_channel ( $ from , $ arguments , 'factundo' , explicit = > 1 , exact_channel = > 1 ) ;
2017-09-02 10:05:11 +02:00
my $ deleted ;
if ( not defined $ trigger ) {
# factoid not found or some error, try to continue and load undo file if it exists
$ deleted = 1 ;
2018-08-09 02:38:57 +02:00
( $ channel , $ trigger ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 2 ) ;
2017-09-02 10:05:11 +02:00
if ( not defined $ trigger ) {
$ trigger = $ channel ;
$ channel = $ from ;
}
$ channel = '.*' if $ channel !~ m/^#/ ;
}
2017-08-26 16:03:01 +02:00
2017-08-27 06:35:46 +02:00
my $ channel_path = $ channel ;
$ channel_path = 'global' if $ channel_path eq '.*' ;
2017-09-05 04:07:10 +02:00
my $ channel_path_safe = safe_filename $ channel_path ;
my $ trigger_safe = safe_filename $ trigger ;
2017-08-26 16:03:01 +02:00
my $ path = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/factlog' ;
2017-09-05 04:07:10 +02:00
my $ undos = eval { retrieve ( "$path/$trigger_safe.$channel_path_safe.undo" ) ; } ;
2017-08-26 16:03:01 +02:00
if ( not $ undos ) {
return "There are no undos available for [$channel] $trigger." ;
}
2017-12-13 00:32:50 +01:00
if ( defined $ list_undos ) {
$ list_undos = 1 if $ list_undos == 0 ;
return $ self - > list_undo_history ( $ undos , $ list_undos ) ;
2017-12-12 12:38:45 +01:00
}
2017-09-05 11:18:02 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
my $ admininfo = $ self - > { pbot } - > { admins } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
if ( $ factoids - > { $ channel } - > { $ trigger } - > { 'locked' } ) {
return "/say $trigger is locked and cannot be reverted." if not defined $ admininfo ;
if ( exists $ factoids - > { $ channel } - > { $ trigger } - > { 'effective-level' }
and $ admininfo - > { level } < $ factoids - > { $ channel } - > { $ trigger } - > { 'effective-level' } ) {
return "/say $trigger is locked with an effective-level higher than your level and cannot be reverted." ;
}
}
2017-12-12 12:38:45 +01:00
if ( defined $ goto_revision ) {
return "Don't be absurd." if $ goto_revision < 1 ;
if ( $ goto_revision > @ { $ undos - > { list } } ) {
if ( @ { $ undos - > { list } } == 1 ) {
return "There is only one revision available for [$channel] $trigger." ;
} else {
return "There are " . @ { $ undos - > { list } } . " revisions available for [$channel] $trigger." ;
}
}
if ( $ goto_revision == $ undos - > { idx } + 1 ) {
return "[$channel] $trigger is already at revision $goto_revision." ;
}
$ undos - > { idx } = $ goto_revision - 1 ;
2017-09-05 04:07:10 +02:00
eval { store $ undos , "$path/$trigger_safe.$channel_path_safe.undo" ; } ;
$ self - > { pbot } - > { logger } - > log ( "Error storing undo: $@\n" ) if $@ ;
2017-12-12 12:38:45 +01:00
} else {
unless ( $ deleted ) {
return "There are no more undos remaining for [$channel] $trigger." if not $ undos - > { idx } ;
$ undos - > { idx } - - ;
eval { store $ undos , "$path/$trigger_safe.$channel_path_safe.undo" ; } ;
$ self - > { pbot } - > { logger } - > log ( "Error storing undo: $@\n" ) if $@ ;
}
2017-09-02 10:05:11 +02:00
}
2017-08-27 06:35:46 +02:00
2017-08-26 16:03:01 +02:00
$ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } = $ undos - > { list } - > [ $ undos - > { idx } ] ;
2017-08-27 06:35:46 +02:00
2017-08-29 08:14:32 +02:00
my $ changes = $ self - > hash_differences_as_string ( $ undos - > { list } - > [ $ undos - > { idx } + 1 ] , $ undos - > { list } - > [ $ undos - > { idx } ] ) ;
$ self - > log_factoid ( $ channel , $ trigger , "$nick!$user\@$host" , "reverted (undo): $changes" , 1 ) ;
return "[$channel] $trigger reverted (revision " . ( $ undos - > { idx } + 1 ) . "): $changes\n" ;
2017-08-26 16:03:01 +02:00
}
sub factredo {
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2017-12-13 00:32:50 +01:00
my $ usage = "Usage: factredo [-l [N]] [-r N] [channel] <keyword> (-l list undo history, optionally starting from N; -r jump to revision N)" ;
2017-12-12 12:38:45 +01:00
my $ getopt_error ;
local $ SIG { __WARN__ } = sub {
$ getopt_error = shift ;
chomp $ getopt_error ;
} ;
my ( $ list_undos , $ goto_revision ) ;
my ( $ ret , $ args ) = GetOptionsFromString ( $ arguments ,
2017-12-13 00:32:50 +01:00
'l:i' = > \ $ list_undos ,
2017-12-12 12:38:45 +01:00
'r=i' = > \ $ goto_revision ) ;
return "/say $getopt_error -- $usage" if defined $ getopt_error ;
return $ usage if @$ args > 2 ;
return $ usage if not @$ args ;
2019-06-23 03:30:40 +02:00
$ arguments = join ( ' ' , map { $ _ = "'$_'" if $ _ =~ m/ / ; $ _ ; } @$ args ) ;
2017-12-12 12:38:45 +01:00
2019-06-10 01:33:27 +02:00
my ( $ channel , $ trigger ) = $ self - > find_factoid_with_optional_channel ( $ from , $ arguments , 'factredo' , explicit = > 1 , exact_channel = > 1 ) ;
2017-08-26 16:03:01 +02:00
return $ channel if not defined $ trigger ; # if $trigger is not defined, $channel is an error message
2017-08-27 06:35:46 +02:00
my $ channel_path = $ channel ;
$ channel_path = 'global' if $ channel_path eq '.*' ;
2017-09-05 04:07:10 +02:00
my $ channel_path_safe = safe_filename $ channel_path ;
my $ trigger_safe = safe_filename $ trigger ;
2017-08-26 16:03:01 +02:00
my $ path = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/factlog' ;
2017-09-05 04:07:10 +02:00
my $ undos = eval { retrieve ( "$path/$trigger_safe.$channel_path_safe.undo" ) ; } ;
2017-08-26 16:03:01 +02:00
if ( not $ undos ) {
return "There are no redos available for [$channel] $trigger." ;
}
2017-12-13 00:32:50 +01:00
if ( defined $ list_undos ) {
$ list_undos = 1 if $ list_undos == 0 ;
return $ self - > list_undo_history ( $ undos , $ list_undos ) ;
2017-12-12 12:38:45 +01:00
}
2017-09-05 11:18:02 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
my $ admininfo = $ self - > { pbot } - > { admins } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
if ( $ factoids - > { $ channel } - > { $ trigger } - > { 'locked' } ) {
return "/say $trigger is locked and cannot be reverted." if not defined $ admininfo ;
if ( exists $ factoids - > { $ channel } - > { $ trigger } - > { 'effective-level' }
and $ admininfo - > { level } < $ factoids - > { $ channel } - > { $ trigger } - > { 'effective-level' } ) {
return "/say $trigger is locked with an effective-level higher than your level and cannot be reverted." ;
}
}
2017-12-12 12:38:45 +01:00
if ( not defined $ goto_revision and $ undos - > { idx } + 1 == @ { $ undos - > { list } } ) {
2017-08-30 15:55:57 +02:00
return "There are no more redos remaining for [$channel] $trigger." ;
2017-08-26 16:03:01 +02:00
}
2017-12-12 12:38:45 +01:00
if ( defined $ goto_revision ) {
return "Don't be absurd." if $ goto_revision < 1 ;
if ( $ goto_revision > @ { $ undos - > { list } } ) {
if ( @ { $ undos - > { list } } == 1 ) {
return "There is only one revision available for [$channel] $trigger." ;
} else {
return "There are " . @ { $ undos - > { list } } . " revisions available for [$channel] $trigger." ;
}
}
if ( $ goto_revision == $ undos - > { idx } + 1 ) {
return "[$channel] $trigger is already at revision $goto_revision." ;
}
$ undos - > { idx } = $ goto_revision - 1 ;
eval { store $ undos , "$path/$trigger_safe.$channel_path_safe.undo" ; } ;
$ self - > { pbot } - > { logger } - > log ( "Error storing undo: $@\n" ) if $@ ;
} else {
$ undos - > { idx } + + ;
eval { store $ undos , "$path/$trigger_safe.$channel_path_safe.undo" ; } ;
$ self - > { pbot } - > { logger } - > log ( "Error storing undo: $@\n" ) if $@ ;
}
2017-08-27 06:40:27 +02:00
2017-08-26 16:03:01 +02:00
$ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } = $ undos - > { list } - > [ $ undos - > { idx } ] ;
2017-08-27 06:40:27 +02:00
2017-08-29 08:14:32 +02:00
my $ changes = $ self - > hash_differences_as_string ( $ undos - > { list } - > [ $ undos - > { idx } - 1 ] , $ undos - > { list } - > [ $ undos - > { idx } ] ) ;
$ self - > log_factoid ( $ channel , $ trigger , "$nick!$user\@$host" , "reverted (redo): $changes" , 1 ) ;
return "[$channel] $trigger restored (revision " . ( $ undos - > { idx } + 1 ) . "): $changes\n" ;
2015-12-13 22:58:01 +01:00
}
2010-06-20 08:16:48 +02:00
sub factset {
my $ self = shift ;
2017-08-27 09:56:55 +02:00
my ( $ from , $ nick , $ user , $ host , $ args ) = @ _ ;
2019-06-10 01:33:27 +02:00
my ( $ channel , $ trigger , $ arguments ) = $ self - > find_factoid_with_optional_channel ( $ from , $ args , 'factset' , usage = > 'Usage: factset [channel] <factoid> [key [value]]' , explicit = > 1 ) ;
2017-08-27 09:56:55 +02:00
return $ channel if not defined $ trigger ; # if $trigger is not defined, $channel is an error message
2018-08-09 02:38:57 +02:00
my $ arglist = $ self - > { pbot } - > { interpreter } - > make_args ( $ arguments ) ;
my ( $ key , $ value ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 2 ) ;
2017-08-27 09:56:55 +02:00
2015-06-06 07:26:02 +02:00
$ channel = '.*' if $ channel !~ /^#/ ;
2019-06-10 01:33:27 +02:00
my ( $ owner_channel , $ owner_trigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ channel , $ trigger , exact_channel = > 1 , exact_trigger = > 1 ) ;
2015-06-06 07:26:02 +02:00
my $ admininfo ;
if ( defined $ owner_channel ) {
$ admininfo = $ self - > { pbot } - > { admins } - > loggedin ( $ owner_channel , "$nick!$user\@$host" ) ;
} else {
2015-09-04 05:43:16 +02:00
$ admininfo = $ self - > { pbot } - > { admins } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
2015-06-06 07:26:02 +02:00
}
2011-01-25 23:40:22 +01:00
my $ level = 0 ;
my $ meta_level = 0 ;
2019-05-28 18:19:42 +02:00
if ( defined $ admininfo ) {
2011-01-25 23:40:22 +01:00
$ level = $ admininfo - > { level } ;
}
2019-05-28 18:19:42 +02:00
if ( defined $ key ) {
if ( defined $ factoid_metadata_levels { $ key } ) {
2011-01-25 23:40:22 +01:00
$ meta_level = $ factoid_metadata_levels { $ key } ;
}
2019-05-28 18:19:42 +02:00
if ( $ meta_level > 0 ) {
if ( $ level == 0 ) {
2011-01-25 23:40:22 +01:00
return "You must login to set '$key'" ;
2019-05-28 18:19:42 +02:00
} elsif ( $ level < $ meta_level ) {
2011-01-25 23:40:22 +01:00
return "You must be at least level $meta_level to set '$key'" ;
}
}
2016-11-17 04:07:01 +01:00
2019-05-28 04:40:24 +02:00
if ( defined $ value and ! $ level and $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { 'locked' } ) {
return "/say $trigger is locked; unlock before setting." ;
}
2016-11-17 04:07:01 +01:00
if ( lc $ key eq 'effective-level' and defined $ value and $ level > 0 ) {
if ( $ value > $ level ) {
return "You cannot set `effective-level` greater than your level, which is $level." ;
} elsif ( $ value < 0 ) {
return "You cannot set a negative effective-level." ;
}
$ self - > { pbot } - > { factoids } - > { factoids } - > set ( $ channel , $ trigger , 'locked' , '1' ) ;
}
if ( lc $ key eq 'locked' and exists $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { 'effective-level' } ) {
if ( $ level < $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { 'effective-level' } ) {
return "You cannot unlock this factoid because its effective-level is greater than your level." ;
}
}
2011-01-25 23:40:22 +01:00
}
2019-05-08 22:06:30 +02:00
if ( defined $ owner_channel ) {
2014-05-18 22:09:05 +02:00
my $ factoid = $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ owner_channel } - > { $ owner_trigger } ;
2011-01-25 23:40:22 +01:00
2013-07-31 15:29:37 +02:00
my ( $ owner ) = $ factoid - > { 'owner' } =~ m/([^!]+)/ ;
2019-05-08 22:06:30 +02:00
if ( ( defined $ value and $ key ne 'action' ) and lc $ nick ne lc $ owner and $ level == 0 ) {
2011-01-25 23:40:22 +01:00
return "You are not the owner of $trigger." ;
}
2015-12-14 07:08:17 +01:00
}
2015-12-13 22:58:01 +01:00
my $ result = $ self - > { pbot } - > { factoids } - > { factoids } - > set ( $ channel , $ trigger , $ key , $ value ) ;
2015-12-22 17:12:59 +01:00
if ( defined $ value and $ result =~ m/set to/ ) {
2017-08-28 04:02:48 +02:00
$ self - > log_factoid ( $ channel , $ trigger , "$nick!$user\@$host" , "set $key to $value" ) ;
2015-12-13 22:58:01 +01:00
}
return $ result ;
2010-06-20 08:16:48 +02:00
}
sub factunset {
my $ self = shift ;
2017-08-28 03:52:14 +02:00
my ( $ from , $ nick , $ user , $ host , $ args ) = @ _ ;
2010-06-20 08:16:48 +02:00
2017-08-28 03:52:14 +02:00
my $ usage = 'Usage: factunset [channel] <factoid> <key>' ;
2010-06-20 08:16:48 +02:00
2019-06-10 01:33:27 +02:00
my ( $ channel , $ trigger , $ arguments ) = $ self - > find_factoid_with_optional_channel ( $ from , $ args , 'factunset' , usage = > $ usage , explicit = > 1 ) ;
2017-08-28 03:52:14 +02:00
return $ channel if not defined $ trigger ; # if $trigger is not defined, $channel is an error message
2019-06-09 22:57:08 +02:00
my ( $ key ) = $ self - > { pbot } - > { interpreter } - > split_line ( $ arguments , strip_quotes = > 1 ) ;
2018-08-09 02:38:57 +02:00
2017-08-28 03:52:14 +02:00
return $ usage if not length $ key ;
2015-06-06 07:26:02 +02:00
2019-06-10 01:33:27 +02:00
my ( $ owner_channel , $ owner_trigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ channel , $ trigger , exact_channel = > 1 , exact_trigger = > 1 ) ;
2015-06-06 07:26:02 +02:00
my $ admininfo ;
if ( defined $ owner_channel ) {
$ admininfo = $ self - > { pbot } - > { admins } - > loggedin ( $ owner_channel , "$nick!$user\@$host" ) ;
} else {
2015-09-04 05:43:16 +02:00
$ admininfo = $ self - > { pbot } - > { admins } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
2015-06-06 07:26:02 +02:00
}
2011-01-25 23:40:22 +01:00
my $ level = 0 ;
my $ meta_level = 0 ;
2019-05-28 18:19:42 +02:00
if ( defined $ admininfo ) {
2011-01-25 23:40:22 +01:00
$ level = $ admininfo - > { level } ;
}
2019-05-28 18:19:42 +02:00
if ( defined $ factoid_metadata_levels { $ key } ) {
2011-01-25 23:40:22 +01:00
$ meta_level = $ factoid_metadata_levels { $ key } ;
}
2019-05-28 18:19:42 +02:00
if ( $ meta_level > 0 ) {
if ( $ level == 0 ) {
2011-01-25 23:40:22 +01:00
return "You must login to unset '$key'" ;
2019-05-28 18:19:42 +02:00
} elsif ( $ level < $ meta_level ) {
2011-01-25 23:40:22 +01:00
return "You must be at least level $meta_level to unset '$key'" ;
}
}
2016-11-17 04:07:01 +01:00
if ( exists $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { 'effective-level' } ) {
if ( lc $ key eq 'locked' ) {
if ( $ level >= $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { 'effective-level' } ) {
$ self - > { pbot } - > { factoids } - > { factoids } - > unset ( $ channel , $ trigger , 'effective-level' ) ;
} else {
return "You cannot unlock this factoid because its effective-level is higher than your level." ;
}
} elsif ( lc $ key eq 'effective-level' ) {
if ( $ level < $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { 'effective-level' } ) {
return "You cannot unset the effective-level because it is higher than your level." ;
}
}
}
2015-12-14 07:08:17 +01:00
my $ oldvalue ;
2019-05-28 18:19:42 +02:00
if ( defined $ owner_channel ) {
2014-05-18 22:09:05 +02:00
my $ factoid = $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ owner_channel } - > { $ owner_trigger } ;
2011-01-25 23:40:22 +01:00
2013-07-31 15:29:37 +02:00
my ( $ owner ) = $ factoid - > { 'owner' } =~ m/([^!]+)/ ;
2019-05-28 18:19:42 +02:00
if ( lc $ nick ne lc $ owner and $ level == 0 ) {
2011-01-25 23:40:22 +01:00
return "You are not the owner of $trigger." ;
}
2015-12-14 07:08:17 +01:00
$ oldvalue = $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ trigger } - > { $ key } ;
2011-01-25 23:40:22 +01:00
}
2017-08-28 03:52:14 +02:00
return "[$channel] $trigger: key '$key' does not exist." if not defined $ oldvalue ;
2015-12-13 22:58:01 +01:00
my $ result = $ self - > { pbot } - > { factoids } - > { factoids } - > unset ( $ channel , $ trigger , $ key ) ;
if ( $ result =~ m/unset/ ) {
$ self - > log_factoid ( $ channel , $ trigger , "$nick!$user\@$host" , "unset $key (value: $oldvalue)" ) ;
}
return $ result ;
2010-03-22 08:33:44 +01:00
}
sub list {
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
my $ text ;
2019-06-26 18:34:19 +02:00
2019-05-28 18:19:42 +02:00
if ( not defined $ arguments ) {
2014-06-01 23:31:54 +02:00
return "Usage: list <modules|factoids|commands|admins>" ;
2010-03-22 08:33:44 +01:00
}
2019-05-28 18:19:42 +02:00
if ( $ arguments =~ /^modules$/i ) {
2010-06-21 05:19:41 +02:00
$ from = '.*' if not defined $ from or $ from !~ /^#/ ;
2010-06-20 08:16:48 +02:00
$ text = "Loaded modules for channel $from: " ;
2014-05-18 22:09:05 +02:00
foreach my $ channel ( sort keys % { $ self - > { pbot } - > { factoids } - > { factoids } - > hash } ) {
foreach my $ command ( sort keys % { $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } } ) {
2019-05-28 18:19:42 +02:00
if ( $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { $ channel } - > { $ command } - > { type } eq 'module' ) {
2010-06-20 08:16:48 +02:00
$ text . = "$command " ;
}
2010-03-22 08:33:44 +01:00
}
}
return $ text ;
}
2019-05-28 18:19:42 +02:00
if ( $ arguments =~ /^commands$/i ) {
2010-03-23 04:09:03 +01:00
$ text = "Registered commands: " ;
2014-05-18 22:09:05 +02:00
foreach my $ command ( sort { $ a - > { name } cmp $ b - > { name } } @ { $ self - > { pbot } - > { commands } - > { handlers } } ) {
2010-03-23 04:09:03 +01:00
$ text . = "$command->{name} " ;
$ text . = "($command->{level}) " if $ command - > { level } > 0 ;
2010-03-22 08:33:44 +01:00
}
return $ text ;
}
2019-05-28 18:19:42 +02:00
if ( $ arguments =~ /^factoids$/i ) {
2014-05-18 22:09:05 +02:00
return "For a list of factoids see " . $ self - > { pbot } - > { factoids } - > export_site ;
2010-03-22 08:33:44 +01:00
}
2019-05-28 18:19:42 +02:00
if ( $ arguments =~ /^admins$/i ) {
2010-03-22 08:33:44 +01:00
$ text = "Admins: " ;
2010-03-29 14:30:35 +02:00
my $ last_channel = "" ;
my $ sep = "" ;
2014-05-18 22:09:05 +02:00
foreach my $ channel ( sort keys % { $ self - > { pbot } - > { admins } - > { admins } - > hash } ) {
2019-05-28 18:19:42 +02:00
if ( $ last_channel ne $ channel ) {
2010-03-29 14:30:35 +02:00
$ text . = $ sep . "Channel " . ( $ channel eq ".*" ? "all" : $ channel ) . ": " ;
$ last_channel = $ channel ;
$ sep = "" ;
}
2014-05-18 22:09:05 +02:00
foreach my $ hostmask ( sort keys % { $ self - > { pbot } - > { admins } - > { admins } - > hash - > { $ channel } } ) {
2010-03-29 14:30:35 +02:00
$ text . = $ sep ;
2015-07-10 08:24:39 +02:00
$ text . = "*" if $ self - > { pbot } - > { admins } - > { admins } - > hash - > { $ channel } - > { $ hostmask } - > { loggedin } ;
2014-05-18 22:09:05 +02:00
$ text . = $ self - > { pbot } - > { admins } - > { admins } - > hash - > { $ channel } - > { $ hostmask } - > { name } . " (" . $ self - > { pbot } - > { admins } - > { admins } - > hash - > { $ channel } - > { $ hostmask } - > { level } . ")" ;
2010-03-29 14:30:35 +02:00
$ sep = "; " ;
}
2010-03-22 08:33:44 +01:00
}
return $ text ;
}
2014-06-01 23:31:54 +02:00
return "Usage: list <modules|commands|factoids|admins>" ;
2010-03-22 08:33:44 +01:00
}
2014-05-23 14:42:23 +02:00
sub factmove {
my $ self = shift ;
2018-08-09 02:38:57 +02:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
my ( $ src_channel , $ source , $ target_channel , $ target ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 5 ) ;
2017-09-05 09:27:28 +02:00
2014-05-23 14:42:23 +02:00
my $ usage = "Usage: factmove <source channel> <source factoid> <target channel/factoid> [target factoid]" ;
2019-05-28 18:19:42 +02:00
if ( not defined $ target_channel ) {
2014-05-23 14:42:23 +02:00
return $ usage ;
}
2019-05-28 18:19:42 +02:00
if ( $ target_channel !~ /^#/ and $ target_channel ne '.*' ) {
if ( defined $ target ) {
2014-05-23 14:42:23 +02:00
return "Unexpected argument '$target' when renaming to '$target_channel'. Perhaps '$target_channel' is missing #s? $usage" ;
}
$ target = $ target_channel ;
$ target_channel = $ src_channel ;
} else {
2019-05-28 18:19:42 +02:00
if ( not defined $ target ) {
2014-05-23 14:42:23 +02:00
$ target = $ source ;
}
}
2019-05-10 03:04:52 +02:00
if ( length $ target > $ self - > { pbot } - > { registry } - > get_value ( 'factoids' , 'max_name_length' ) ) {
2017-09-05 09:27:28 +02:00
return "/say $nick: I don't think the factoid name needs to be that long." ;
}
2019-05-10 03:04:52 +02:00
if ( length $ target_channel > $ self - > { pbot } - > { registry } - > get_value ( 'factoids' , 'max_channel_length' ) ) {
2017-09-05 09:27:28 +02:00
return "/say $nick: I don't think the channel name needs to be that long." ;
}
2019-06-10 01:33:27 +02:00
my ( $ found_src_channel , $ found_source ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ src_channel , $ source , exact_channel = > 1 , exact_trigger = > 1 ) ;
2014-05-23 14:42:23 +02:00
2019-05-28 18:19:42 +02:00
if ( not defined $ found_src_channel ) {
2014-05-23 14:42:23 +02:00
return "Source factoid $source not found in channel $src_channel" ;
}
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
my ( $ owner ) = $ factoids - > { $ found_src_channel } - > { $ found_source } - > { 'owner' } =~ m/([^!]+)/ ;
2019-05-28 18:19:42 +02:00
if ( ( lc $ nick ne lc $ owner ) and ( not $ self - > { pbot } - > { admins } - > loggedin ( $ found_src_channel , "$nick!$user\@$host" ) ) ) {
2014-05-23 14:42:23 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempted to move [$found_src_channel] $found_source (not owner)\n" ) ;
my $ chan = ( $ found_src_channel eq '.*' ? 'the global channel' : $ found_src_channel ) ;
2014-06-01 23:31:54 +02:00
return "You are not the owner of $found_source for $chan" ;
2014-05-23 14:42:23 +02:00
}
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ found_src_channel } - > { $ found_source } - > { 'locked' } ) {
2017-09-02 09:14:13 +02:00
return "/say $found_source is locked; unlock before moving." ;
2014-05-23 14:42:23 +02:00
}
2019-06-10 01:33:27 +02:00
my ( $ found_target_channel , $ found_target ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ target_channel , $ target , exact_channel = > 1 , exact_trigger = > 1 ) ;
2014-05-23 14:42:23 +02:00
2019-05-28 18:19:42 +02:00
if ( defined $ found_target_channel ) {
2014-05-23 14:42:23 +02:00
return "Target factoid $target already exists in channel $target_channel" ;
}
2019-06-10 01:33:27 +02:00
my ( $ overchannel , $ overtrigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( '.*' , $ target , exact_channel = > 1 , exact_trigger = > 1 ) ;
2019-05-28 18:19:42 +02:00
if ( defined $ overtrigger and $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { '.*' } - > { $ overtrigger } - > { 'nooverride' } ) {
2017-11-30 00:55:53 +01:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempt to override $target\n" ) ;
return "/say $target already exists for the global channel and cannot be overridden for " . ( $ target_channel eq '.*' ? 'the global channel' : $ target_channel ) . "." ;
}
2017-12-03 00:05:56 +01:00
if ( $ self - > { pbot } - > { commands } - > exists ( $ target ) ) {
return "/say $target already exists as a built-in command." ;
}
2014-05-23 14:42:23 +02:00
$ target_channel = lc $ target_channel ;
2017-08-24 04:18:14 +02:00
$ target_channel = '.*' if $ target_channel !~ /^#/ ;
2014-05-23 14:42:23 +02:00
$ factoids - > { $ target_channel } - > { $ target } = $ factoids - > { $ found_src_channel } - > { $ found_source } ;
delete $ factoids - > { $ found_src_channel } - > { $ found_source } ;
$ self - > { pbot } - > { factoids } - > save_factoids ;
2015-02-16 05:17:36 +01:00
$ found_src_channel = 'global' if $ found_src_channel eq '.*' ;
$ target_channel = 'global' if $ target_channel eq '.*' ;
2019-05-28 18:19:42 +02:00
if ( $ src_channel eq $ target_channel ) {
2015-12-13 22:58:01 +01:00
$ self - > log_factoid ( $ target_channel , $ target , "$nick!$user\@$host" , "renamed from $found_source to $target" ) ;
2019-06-26 18:34:19 +02:00
return "[$found_src_channel] $found_source renamed to $target" ;
2014-05-23 14:42:23 +02:00
} else {
2017-08-31 01:07:33 +02:00
$ self - > log_factoid ( $ found_src_channel , $ found_source , "$nick!$user\@$host" , "moved from $found_src_channel/$found_source to $target_channel/$target" ) ;
2015-12-13 22:58:01 +01:00
$ self - > log_factoid ( $ target_channel , $ target , "$nick!$user\@$host" , "moved from $found_src_channel/$found_source to $target_channel/$target" ) ;
2014-05-23 14:42:23 +02:00
return "[$found_src_channel] $found_source moved to [$target_channel] $target" ;
}
}
2010-06-23 04:15:13 +02:00
sub factalias {
2010-03-22 08:33:44 +01:00
my $ self = shift ;
2018-08-09 02:38:57 +02:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
my ( $ chan , $ alias , $ command ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 3 ) ;
2017-12-12 00:24:37 +01:00
if ( defined $ chan and not ( $ chan eq '.*' or $ chan =~ m/^#/ ) ) {
# $chan doesn't look like a channel, so shift everything right
# and replace $chan with $from
if ( defined $ command and length $ command ) {
$ command = "$alias $command" ;
} else {
$ command = $ alias ;
}
$ alias = $ chan ;
$ chan = $ from ;
2010-03-22 08:33:44 +01:00
}
2010-06-20 08:16:48 +02:00
2018-08-09 02:38:57 +02:00
$ chan = '.*' if $ chan !~ /^#/ ;
2017-12-12 00:24:37 +01:00
if ( not length $ alias or not length $ command ) {
return "Usage: factalias [channel] <keyword> <command>" ;
}
2010-07-04 09:26:43 +02:00
2019-05-10 03:04:52 +02:00
if ( length $ alias > $ self - > { pbot } - > { registry } - > get_value ( 'factoids' , 'max_name_length' ) ) {
2017-09-05 09:27:28 +02:00
return "/say $nick: I don't think the factoid name needs to be that long." ;
}
2019-05-10 03:04:52 +02:00
if ( length $ chan > $ self - > { pbot } - > { registry } - > get_value ( 'factoids' , 'max_channel_length' ) ) {
2017-09-05 09:27:28 +02:00
return "/say $nick: I don't think the channel name needs to be that long." ;
}
2019-06-10 01:33:27 +02:00
my ( $ channel , $ alias_trigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ chan , $ alias , exact_channel = > 1 , exact_trigger = > 1 ) ;
2019-06-26 18:34:19 +02:00
2019-05-28 18:19:42 +02:00
if ( defined $ alias_trigger ) {
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "attempt to overwrite existing command\n" ) ;
2014-06-01 23:31:54 +02:00
return "'$alias_trigger' already exists for channel $channel" ;
2010-03-22 08:33:44 +01:00
}
2017-11-30 00:55:53 +01:00
2019-06-10 01:33:27 +02:00
my ( $ overchannel , $ overtrigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( '.*' , $ alias , exact_channel = > 1 , exact_trigger = > 1 ) ;
2019-05-28 18:19:42 +02:00
if ( defined $ overtrigger and $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { '.*' } - > { $ overtrigger } - > { 'nooverride' } ) {
2017-11-30 00:55:53 +01:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempt to override $alias\n" ) ;
return "/say $alias already exists for the global channel and cannot be overridden for " . ( $ chan eq '.*' ? 'the global channel' : $ chan ) . "." ;
}
2017-12-03 00:05:56 +01:00
if ( $ self - > { pbot } - > { commands } - > exists ( $ alias ) ) {
return "/say $alias already exists as a built-in command." ;
}
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { factoids } - > add_factoid ( 'text' , $ chan , "$nick!$user\@$host" , $ alias , "/call $command" ) ;
2010-06-20 08:16:48 +02:00
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host [$chan] aliased $alias => $command\n" ) ;
$ self - > { pbot } - > { factoids } - > save_factoids ( ) ;
2019-06-26 18:34:19 +02:00
return "'$alias' aliases '$command' for " . ( $ chan eq '.*' ? 'the global channel' : $ chan ) ;
2010-03-22 08:33:44 +01:00
}
sub add_regex {
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2014-05-18 22:09:05 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-03-22 08:33:44 +01:00
my ( $ keyword , $ text ) = $ arguments =~ /^(.*?)\s+(.*)$/ if defined $ arguments ;
2010-06-21 05:19:41 +02:00
$ from = '.*' if not defined $ from or $ from !~ /^#/ ;
2010-06-20 08:16:48 +02:00
2019-05-28 18:19:42 +02:00
if ( not defined $ keyword ) {
2010-03-22 08:33:44 +01:00
$ text = "" ;
2014-05-18 22:19:30 +02:00
foreach my $ trigger ( sort keys % { $ factoids - > { $ from } } ) {
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ from } - > { $ trigger } - > { type } eq 'regex' ) {
2010-06-20 08:16:48 +02:00
$ text . = $ trigger . " " ;
2010-03-22 08:33:44 +01:00
}
}
2010-06-20 08:16:48 +02:00
return "Stored regexs for channel $from: $text" ;
2010-03-22 08:33:44 +01:00
}
2019-05-28 18:19:42 +02:00
if ( not defined $ text ) {
2010-06-21 16:48:40 +02:00
return "Usage: regex <regex> <command>" ;
2010-03-22 08:33:44 +01:00
}
2019-06-10 01:33:27 +02:00
my ( $ channel , $ trigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ from , $ keyword , exact_channel = > 1 , exact_trigger = > 1 ) ;
2010-06-20 08:16:48 +02:00
2019-05-28 18:19:42 +02:00
if ( defined $ trigger ) {
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempt to overwrite $trigger\n" ) ;
2017-09-02 09:14:13 +02:00
return "/say $trigger already exists for channel $channel." ;
2010-03-22 08:33:44 +01:00
}
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { factoids } - > add_factoid ( 'regex' , $ from , "$nick!$user\@$host" , $ keyword , $ text ) ;
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host added [$keyword] => [$text]\n" ) ;
2017-09-02 09:14:13 +02:00
return "/say $keyword added." ;
2010-03-22 08:33:44 +01:00
}
2010-06-23 04:15:13 +02:00
sub factadd {
2010-03-22 08:33:44 +01:00
my $ self = shift ;
2018-08-09 02:38:57 +02:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2019-06-03 07:34:17 +02:00
my ( $ from_chan , $ keyword , $ text , $ force ) ;
2017-08-02 06:33:57 +02:00
2018-08-09 02:38:57 +02:00
my @ arglist = @ { $ stuff - > { arglist } } ;
2017-09-05 09:27:28 +02:00
2018-08-09 02:38:57 +02:00
if ( @ arglist ) {
2019-06-03 07:34:17 +02:00
# check for -f since we allow it to be before optional channel argument
if ( $ arglist [ 0 ] eq '-f' ) {
$ force = 1 ;
$ self - > { pbot } - > { interpreter } - > shift_arg ( \ @ arglist ) ;
}
# check if this is an optional channel argument
2018-08-09 02:38:57 +02:00
if ( $ arglist [ 0 ] =~ m/(?:^#|^global$|^\.\*$)/i ) {
2019-06-03 07:34:17 +02:00
$ from_chan = $ self - > { pbot } - > { interpreter } - > shift_arg ( \ @ arglist ) ;
2018-08-09 02:38:57 +02:00
} else {
$ from_chan = $ from ;
2017-08-02 06:33:57 +02:00
}
2019-06-03 07:34:17 +02:00
# check for -f again since we also allow it to appear after the channel argument
if ( $ arglist [ 0 ] eq '-f' ) {
$ force = 1 ;
$ self - > { pbot } - > { interpreter } - > shift_arg ( \ @ arglist ) ;
}
# now this is the keyword
$ keyword = $ self - > { pbot } - > { interpreter } - > shift_arg ( \ @ arglist ) ;
2019-07-01 02:45:05 +02:00
# check for -url
if ( $ arglist [ 0 ] eq '-url' ) {
# discard it
2019-06-03 07:34:17 +02:00
$ self - > { pbot } - > { interpreter } - > shift_arg ( \ @ arglist ) ;
2019-07-01 02:45:05 +02:00
# the URL is the remaining arguments
( $ url ) = $ self - > { pbot } - > { interpreter } - > split_args ( \ @ arglist , 1 ) ;
# create a UserAgent
my $ ua = LWP::UserAgent - > new ( timeout = > 10 ) ;
# get the factoid's text from the URL
my $ response = $ ua - > get ( $ url ) ;
# process the response
if ( $ response - > is_success ) {
$ text = $ response - > decoded_content ;
} else {
return "Failed to get URL: " . $ response - > status_line ;
}
} else {
# check for optional "is" and discard
if ( lc $ arglist [ 0 ] eq 'is' ) {
$ self - > { pbot } - > { interpreter } - > shift_arg ( \ @ arglist ) ;
}
# and the text is the remaining arguments with quotes preserved
( $ text ) = $ self - > { pbot } - > { interpreter } - > split_args ( \ @ arglist , 1 ) ;
}
2017-08-02 06:33:57 +02:00
}
2010-03-22 08:33:44 +01:00
2019-05-28 18:19:42 +02:00
if ( not defined $ from_chan or not defined $ text or not defined $ keyword ) {
2019-06-03 07:34:17 +02:00
return "Usage: factadd [-f] [channel] <keyword> <factoid>; -f to force overwrite" ;
2010-03-22 08:33:44 +01:00
}
2018-08-01 02:23:15 +02:00
$ from_chan = '.*' if $ from_chan !~ /^#/ ;
2017-01-30 03:01:26 +01:00
2019-05-10 03:04:52 +02:00
if ( length $ keyword > $ self - > { pbot } - > { registry } - > get_value ( 'factoids' , 'max_name_length' ) ) {
2017-09-05 09:27:28 +02:00
return "/say $nick: I don't think the factoid name needs to be that long." ;
}
2019-05-10 03:04:52 +02:00
if ( length $ from_chan > $ self - > { pbot } - > { registry } - > get_value ( 'factoids' , 'max_channel_length' ) ) {
2017-09-05 09:27:28 +02:00
return "/say $nick: I don't think the channel needs to be that long." ;
}
2017-01-30 03:01:26 +01:00
$ from_chan = '.*' if lc $ from_chan eq 'global' ;
2010-06-29 07:48:46 +02:00
$ from_chan = '.*' if not $ from_chan =~ m/^#/ ;
2018-08-09 02:58:53 +02:00
my $ keyword_text = $ keyword =~ / / ? "\"$keyword\"" : $ keyword ;
2019-06-10 01:33:27 +02:00
my ( $ channel , $ trigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( $ from_chan , $ keyword , exact_channel = > 1 , exact_trigger = > 1 ) ;
2019-05-28 18:19:42 +02:00
if ( defined $ trigger ) {
2019-06-03 07:34:17 +02:00
if ( not $ force ) {
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempt to overwrite $keyword\n" ) ;
return "/say $keyword_text already exists for " . ( $ from_chan eq '.*' ? 'the global channel' : $ from_chan ) . "." ;
} else {
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
if ( $ factoids - > { $ channel } - > { $ trigger } - > { 'locked' } ) {
2019-06-03 17:01:52 +02:00
return "/say $keyword_text is locked; unlock before overwriting." ;
2019-06-03 07:34:17 +02:00
}
my ( $ owner ) = $ factoids - > { $ channel } - > { $ trigger } - > { 'owner' } =~ m/([^!]+)/ ;
if ( ( lc $ nick ne lc $ owner ) and ( not $ self - > { pbot } - > { admins } - > loggedin ( $ channel , "$nick!$user\@$host" ) ) ) {
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempted to overwrite $trigger [not owner]\n" ) ;
my $ chan = ( $ channel eq '.*' ? 'the global channel' : $ channel ) ;
2019-06-03 17:01:52 +02:00
return "You are not the owner of $keyword_text for $chan; cannot overwrite" ;
2019-06-03 07:34:17 +02:00
}
}
2014-07-11 14:56:17 +02:00
}
2019-06-10 01:33:27 +02:00
( $ channel , $ trigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( '.*' , $ keyword , exact_channel = > 1 , exact_trigger = > 1 ) ;
2019-05-28 18:19:42 +02:00
if ( defined $ trigger and $ self - > { pbot } - > { factoids } - > { factoids } - > hash - > { '.*' } - > { $ trigger } - > { 'nooverride' } ) {
2018-08-09 02:58:53 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempt to override $keyword_text\n" ) ;
return "/say $keyword_text already exists for the global channel and cannot be overridden for " . ( $ from_chan eq '.*' ? 'the global channel' : $ from_chan ) . "." ;
2010-03-22 08:33:44 +01:00
}
2017-12-03 00:05:56 +01:00
if ( $ self - > { pbot } - > { commands } - > exists ( $ keyword ) ) {
2018-08-09 02:58:53 +02:00
return "/say $keyword_text already exists as a built-in command." ;
2017-12-03 00:05:56 +01:00
}
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { factoids } - > add_factoid ( 'text' , $ from_chan , "$nick!$user\@$host" , $ keyword , $ text ) ;
2019-06-26 18:34:19 +02:00
2018-08-09 02:58:53 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host added [$from_chan] $keyword_text => $text\n" ) ;
return "/say $keyword_text added to " . ( $ from_chan eq '.*' ? 'global channel' : $ from_chan ) . "." ;
2010-03-22 08:33:44 +01:00
}
2010-06-23 04:15:13 +02:00
sub factrem {
2010-06-20 08:16:48 +02:00
my $ self = shift ;
2018-08-09 02:38:57 +02:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2014-05-18 22:09:05 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-06-20 08:16:48 +02:00
2018-08-09 02:38:57 +02:00
my ( $ from_chan , $ from_trig ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 2 ) ;
2010-06-20 08:16:48 +02:00
2017-08-27 09:56:55 +02:00
if ( not defined $ from_trig ) {
$ from_trig = $ from_chan ;
2017-08-02 06:33:57 +02:00
$ from_chan = $ from ;
2015-07-24 02:46:01 +02:00
}
2019-06-10 01:33:27 +02:00
my ( $ channel , $ trigger ) = $ self - > find_factoid_with_optional_channel ( $ from , $ arguments , 'factrem' , explicit = > 1 ) ;
2017-08-27 09:56:55 +02:00
return $ channel if not defined $ trigger ; # if $trigger is not defined, $channel is an error message
2010-06-20 08:16:48 +02:00
2015-12-13 22:58:01 +01:00
$ channel = '.*' if $ channel eq 'global' ;
2017-08-09 06:18:00 +02:00
$ from_chan = '.*' if $ channel eq 'global' ;
2015-12-13 22:58:01 +01:00
2018-08-09 02:58:53 +02:00
my $ trigger_text = $ trigger =~ / / ? "\"$trigger\"" : $ trigger ; ;
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ channel } - > { $ trigger } - > { type } eq 'module' ) {
2018-08-09 02:58:53 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempted to remove $trigger_text [not factoid]\n" ) ;
return "/say $trigger_text is not a factoid." ;
2010-06-20 08:16:48 +02:00
}
2017-08-02 06:33:57 +02:00
if ( $ channel =~ /^#/ and $ from_chan =~ /^#/ and $ channel ne $ from_chan ) {
2018-08-09 02:58:53 +02:00
return "/say $trigger_text belongs to $channel, but this is $from_chan. Please switch to $channel or /msg to remove this factoid." ;
2017-08-02 06:33:57 +02:00
}
2014-05-18 22:19:30 +02:00
my ( $ owner ) = $ factoids - > { $ channel } - > { $ trigger } - > { 'owner' } =~ m/([^!]+)/ ;
2013-07-31 15:29:37 +02:00
2019-05-28 18:19:42 +02:00
if ( ( lc $ nick ne lc $ owner ) and ( not $ self - > { pbot } - > { admins } - > loggedin ( $ channel , "$nick!$user\@$host" ) ) ) {
2018-08-09 02:58:53 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempted to remove $trigger_text [not owner]\n" ) ;
2010-06-29 09:14:26 +02:00
my $ chan = ( $ channel eq '.*' ? 'the global channel' : $ channel ) ;
2018-08-09 02:58:53 +02:00
return "You are not the owner of $trigger_text for $chan" ;
2010-06-20 08:16:48 +02:00
}
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ channel } - > { $ trigger } - > { 'locked' } ) {
2018-08-09 02:58:53 +02:00
return "/say $trigger_text is locked; unlock before deleting." ;
2013-09-13 23:48:19 +02:00
}
2014-05-18 22:19:30 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host removed [$channel][$trigger][" . $ factoids - > { $ channel } - > { $ trigger } - > { action } . "]\n" ) ;
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { factoids } - > remove_factoid ( $ channel , $ trigger ) ;
2017-08-26 16:03:01 +02:00
$ self - > log_factoid ( $ channel , $ trigger , "$nick!$user\@$host" , "deleted" , 1 ) ;
2018-08-09 02:58:53 +02:00
return "/say $trigger_text removed from " . ( $ channel eq '.*' ? 'the global channel' : $ channel ) . "." ;
2010-06-20 08:16:48 +02:00
}
2010-03-22 08:33:44 +01:00
sub histogram {
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2014-05-24 14:01:59 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-03-22 08:33:44 +01:00
my % hash ;
my $ factoid_count = 0 ;
2014-05-24 14:01:59 +02:00
foreach my $ channel ( keys %$ factoids ) {
foreach my $ command ( keys % { $ factoids - > { $ channel } } ) {
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ channel } - > { $ command } - > { type } eq 'text' ) {
2014-05-24 14:01:59 +02:00
$ hash { $ factoids - > { $ channel } - > { $ command } - > { owner } } + + ;
$ factoid_count + + ;
}
2010-03-22 08:33:44 +01:00
}
}
my $ text ;
my $ i = 0 ;
foreach my $ owner ( sort { $ hash { $ b } <=> $ hash { $ a } } keys % hash ) {
my $ percent = int ( $ hash { $ owner } / $ factoid_count * 100 ) ;
2019-06-26 18:34:19 +02:00
$ text . = "$owner: $hash{$owner} ($percent" . "%)\n" ;
2010-03-22 08:33:44 +01:00
$ i + + ;
last if $ i >= 10 ;
}
2017-09-02 09:14:13 +02:00
return "/say $factoid_count factoids, top 10 submitters:\n$text" ;
2010-03-22 08:33:44 +01:00
}
2010-06-23 04:15:13 +02:00
sub factshow {
2010-03-22 08:33:44 +01:00
my $ self = shift ;
2018-08-09 02:38:57 +02:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2014-05-18 22:09:05 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-03-22 08:33:44 +01:00
2019-06-09 22:57:52 +02:00
$ stuff - > { preserve_whitespace } = 1 ;
2018-08-09 02:38:57 +02:00
my ( $ chan , $ trig ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 2 ) ;
2010-06-21 16:41:39 +02:00
2015-07-22 00:07:56 +02:00
if ( not defined $ trig ) {
$ trig = $ chan ;
2017-08-24 04:18:14 +02:00
$ chan = $ from ;
2015-07-22 00:07:56 +02:00
}
2017-08-27 09:56:55 +02:00
my ( $ channel , $ trigger ) = $ self - > find_factoid_with_optional_channel ( $ from , $ arguments , 'factshow' ) ;
return $ channel if not defined $ trigger ; # if $trigger is not defined, $channel is an error message
2015-07-22 00:07:56 +02:00
2018-08-09 02:58:53 +02:00
my $ trigger_text = $ trigger =~ / / ? "\"$trigger\"" : $ trigger ;
my $ result = "$trigger_text: " . $ factoids - > { $ channel } - > { $ trigger } - > { action } ;
2014-03-05 15:30:02 +01:00
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ channel } - > { $ trigger } - > { type } eq 'module' ) {
2014-03-05 15:30:02 +01:00
$ result . = ' [module]' ;
2010-03-22 08:33:44 +01:00
}
2017-08-24 04:18:14 +02:00
$ channel = 'global' if $ channel eq '.*' ;
$ chan = 'global' if $ chan eq '.*' ;
2017-08-12 10:28:55 +02:00
$ result = "[$channel] $result" if $ channel ne $ chan ;
2014-03-05 15:30:02 +01:00
return $ result ;
2010-03-22 08:33:44 +01:00
}
2015-12-13 23:24:25 +01:00
sub factlog {
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2015-12-15 01:13:43 +01:00
my $ usage = "Usage: factlog [-h] [-t] [channel] <keyword>; -h show full hostmask; -t show actual timestamp instead of relative" ;
return $ usage if not $ arguments ;
my $ getopt_error ;
local $ SIG { __WARN__ } = sub {
$ getopt_error = shift ;
chomp $ getopt_error ;
} ;
my ( $ show_hostmask , $ actual_timestamp ) ;
my ( $ ret , $ args ) = GetOptionsFromString ( $ arguments ,
'h' = > \ $ show_hostmask ,
't' = > \ $ actual_timestamp ) ;
2017-09-02 09:14:13 +02:00
return "/say $getopt_error -- $usage" if defined $ getopt_error ;
2015-12-15 01:13:43 +01:00
return "Too many arguments -- $usage" if @$ args > 2 ;
return "Missing argument -- $usage" if not @$ args ;
2019-06-23 03:30:40 +02:00
$ args = join ( ' ' , map { $ _ = "'$_'" if $ _ =~ m/ / ; $ _ ; } @$ args ) ;
2019-06-10 06:50:40 +02:00
my ( $ channel , $ trigger ) = $ self - > find_factoid_with_optional_channel ( $ from , $ args , 'factlog' , usage = > $ usage , exact_channel = > 1 ) ;
2017-09-02 10:05:11 +02:00
if ( not defined $ trigger ) {
# factoid not found or some error, try to continue and load factlog file if it exists
2019-06-10 06:50:40 +02:00
my $ arglist = $ self - > { pbot } - > { interpreter } - > make_args ( $ args ) ;
2018-08-09 02:38:57 +02:00
( $ channel , $ trigger ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 2 ) ;
2017-09-02 10:05:11 +02:00
if ( not defined $ trigger ) {
$ trigger = $ channel ;
$ channel = $ from ;
}
$ channel = '.*' if $ channel !~ m/^#/ ;
}
2015-12-13 23:24:25 +01:00
my $ path = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/factlog' ;
$ channel = 'global' if $ channel eq '.*' ;
2017-09-05 04:07:10 +02:00
my $ channel_safe = safe_filename $ channel ;
my $ trigger_safe = safe_filename $ trigger ;
open my $ fh , "< $path/$trigger_safe.$channel_safe" or do {
2017-12-11 23:24:29 +01:00
$ self - > { pbot } - > { logger } - > log ( "Could not open $path/$trigger_safe.$channel_safe: $!\n" ) ;
2015-12-13 23:24:25 +01:00
$ channel = 'the global channel' if $ channel eq 'global' ;
return "No factlog available for $trigger in $channel." ;
} ;
2017-08-28 04:00:31 +02:00
my @ entries ;
2015-12-13 23:24:25 +01:00
while ( my $ line = <$fh> ) {
2017-09-02 09:39:29 +02:00
my ( $ timestamp , $ hostmask , $ msg ) = split /\s+/ , $ line , 3 ;
2015-12-15 01:13:43 +01:00
if ( not $ show_hostmask ) {
$ hostmask =~ s/!.*$// ;
}
if ( $ actual_timestamp ) {
$ timestamp = strftime "%a %b %e %H:%M:%S %Z %Y" , localtime $ timestamp ;
} else {
$ timestamp = concise ago gettimeofday - $ timestamp ;
}
2017-08-28 04:00:31 +02:00
push @ entries , "[$timestamp] $hostmask $msg\n" ;
2015-12-13 23:24:25 +01:00
}
close $ fh ;
2017-08-28 04:00:31 +02:00
my $ result = join "" , reverse @ entries ;
2015-12-13 23:24:25 +01:00
return $ result ;
}
2010-06-23 04:15:13 +02:00
sub factinfo {
2010-03-22 08:33:44 +01:00
my $ self = shift ;
2018-08-09 02:38:57 +02:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2014-05-18 22:09:05 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-03-22 08:33:44 +01:00
2018-08-09 02:38:57 +02:00
my ( $ chan , $ trig ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 2 ) ;
2010-03-22 08:33:44 +01:00
2015-07-22 00:07:56 +02:00
if ( not defined $ trig ) {
$ trig = $ chan ;
2017-08-27 09:56:55 +02:00
$ chan = $ from ;
2010-03-22 08:33:44 +01:00
}
2017-08-27 09:56:55 +02:00
my ( $ channel , $ trigger ) = $ self - > find_factoid_with_optional_channel ( $ from , $ arguments , 'factinfo' ) ;
return $ channel if not defined $ trigger ; # if $trigger is not defined, $channel is an error message
2015-07-22 00:07:56 +02:00
2014-05-18 22:19:30 +02:00
my $ created_ago = ago ( gettimeofday - $ factoids - > { $ channel } - > { $ trigger } - > { created_on } ) ;
my $ ref_ago = ago ( gettimeofday - $ factoids - > { $ channel } - > { $ trigger } - > { last_referenced_on } ) if defined $ factoids - > { $ channel } - > { $ trigger } - > { last_referenced_on } ;
2010-06-21 12:44:15 +02:00
2010-06-29 08:12:52 +02:00
$ chan = ( $ channel eq '.*' ? 'global channel' : $ channel ) ;
2010-06-21 15:28:54 +02:00
2010-03-22 08:33:44 +01:00
# factoid
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ channel } - > { $ trigger } - > { type } eq 'text' ) {
2019-06-26 18:34:19 +02:00
return "/say $trigger: Factoid submitted by " . $ factoids - > { $ channel } - > { $ trigger } - > { owner } . " for $chan on " . localtime ( $ factoids - > { $ channel } - > { $ trigger } - > { created_on } ) . " [$created_ago], " . ( defined $ factoids - > { $ channel } - > { $ trigger } - > { edited_by } ? "last edited by $factoids->{$channel}->{$trigger}->{edited_by} on " . localtime ( $ factoids - > { $ channel } - > { $ trigger } - > { edited_on } ) . " [" . ago ( gettimeofday - $ factoids - > { $ channel } - > { $ trigger } - > { edited_on } ) . "], " : "" ) . "referenced " . $ factoids - > { $ channel } - > { $ trigger } - > { ref_count } . " times (last by " . $ factoids - > { $ channel } - > { $ trigger } - > { ref_user } . ( exists $ factoids - > { $ channel } - > { $ trigger } - > { last_referenced_on } ? " on " . localtime ( $ factoids - > { $ channel } - > { $ trigger } - > { last_referenced_on } ) . " [$ref_ago]" : "" ) . ")" ;
2010-03-22 08:33:44 +01:00
}
# module
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ channel } - > { $ trigger } - > { type } eq 'module' ) {
2014-12-30 08:15:46 +01:00
my $ module_repo = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'module_repo' ) ;
2015-03-16 04:12:44 +01:00
$ module_repo . = "$factoids->{$channel}->{$trigger}->{workdir}/" if exists $ factoids - > { $ channel } - > { $ trigger } - > { workdir } ;
2019-06-26 18:34:19 +02:00
return "/say $trigger: Module loaded by " . $ factoids - > { $ channel } - > { $ trigger } - > { owner } . " for $chan on " . localtime ( $ factoids - > { $ channel } - > { $ trigger } - > { created_on } ) . " [$created_ago] -> $module_repo" . $ factoids - > { $ channel } - > { $ trigger } - > { action } . ", used " . $ factoids - > { $ channel } - > { $ trigger } - > { ref_count } . " times (last by " . $ factoids - > { $ channel } - > { $ trigger } - > { ref_user } . ( exists $ factoids - > { $ channel } - > { $ trigger } - > { last_referenced_on } ? " on " . localtime ( $ factoids - > { $ channel } - > { $ trigger } - > { last_referenced_on } ) . " [$ref_ago]" : "" ) . ")" ;
2010-03-22 08:33:44 +01:00
}
# regex
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ channel } - > { $ trigger } - > { type } eq 'regex' ) {
2019-06-26 18:34:19 +02:00
return "/say $trigger: Regex created by " . $ factoids - > { $ channel } - > { $ trigger } - > { owner } . " for $chan on " . localtime ( $ factoids - > { $ channel } - > { $ trigger } - > { created_on } ) . " [$created_ago], " . ( defined $ factoids - > { $ channel } - > { $ trigger } - > { edited_by } ? "last edited by $factoids->{$channel}->{$trigger}->{edited_by} on " . localtime ( $ factoids - > { $ channel } - > { $ trigger } - > { edited_on } ) . " [" . ago ( gettimeofday - $ factoids - > { $ channel } - > { $ trigger } - > { edited_on } ) . "], " : "" ) . " used " . $ factoids - > { $ channel } - > { $ trigger } - > { ref_count } . " times (last by " . $ factoids - > { $ channel } - > { $ trigger } - > { ref_user } . ( exists $ factoids - > { $ channel } - > { $ trigger } - > { last_referenced_on } ? " on " . localtime ( $ factoids - > { $ channel } - > { $ trigger } - > { last_referenced_on } ) . " [$ref_ago]" : "" ) . ")" ;
2010-03-22 08:33:44 +01:00
}
2017-09-02 09:14:13 +02:00
return "/say $arguments is not a factoid or a module" ;
2010-03-22 08:33:44 +01:00
}
sub top20 {
my $ self = shift ;
2018-08-09 02:38:57 +02:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2014-05-18 22:09:05 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-03-22 08:33:44 +01:00
my % hash = ( ) ;
my $ text = "" ;
my $ i = 0 ;
2018-08-09 02:38:57 +02:00
my ( $ channel , $ args ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 2 ) ;
2011-01-27 01:49:36 +01:00
2019-05-28 18:19:42 +02:00
if ( not defined $ channel ) {
2011-01-27 01:49:36 +01:00
return "Usage: top20 <channel> [nick or 'recent']" ;
}
2019-05-28 18:19:42 +02:00
if ( not defined $ args ) {
2011-01-27 01:49:36 +01:00
foreach my $ chan ( sort keys % { $ factoids } ) {
next if lc $ chan ne lc $ channel ;
2014-05-18 22:19:30 +02:00
foreach my $ command ( sort { $ factoids - > { $ chan } - > { $ b } { ref_count } <=> $ factoids - > { $ chan } - > { $ a } { ref_count } } keys % { $ factoids - > { $ chan } } ) {
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ chan } - > { $ command } { ref_count } > 0 and $ factoids - > { $ chan } - > { $ command } { type } eq 'text' ) {
2014-05-18 22:19:30 +02:00
$ text . = "$command ($factoids->{$chan}->{$command}{ref_count}) " ;
2011-01-27 01:49:36 +01:00
$ i + + ;
last if $ i >= 20 ;
}
2010-03-22 08:33:44 +01:00
}
2011-01-27 01:49:36 +01:00
$ channel = "the global channel" if $ channel eq '.*' ;
$ text = "Top $i referenced factoids for $channel: $text" if $ i > 0 ;
return $ text ;
2010-03-22 08:33:44 +01:00
}
} else {
2019-05-28 18:19:42 +02:00
if ( lc $ args eq "recent" ) {
2011-01-27 01:49:36 +01:00
foreach my $ chan ( sort keys % { $ factoids } ) {
next if lc $ chan ne lc $ channel ;
2014-05-18 22:19:30 +02:00
foreach my $ command ( sort { $ factoids - > { $ chan } - > { $ b } { created_on } <=> $ factoids - > { $ chan } - > { $ a } { created_on } } keys % { $ factoids - > { $ chan } } ) {
2015-12-22 17:12:59 +01:00
my $ ago = concise ago gettimeofday - $ factoids - > { $ chan } - > { $ command } - > { created_on } ;
my $ owner = $ factoids - > { $ chan } - > { $ command } - > { owner } ;
$ owner =~ s/!.*$// ;
$ text . = " $command [$ago by $owner]\n" ;
2011-01-27 01:49:36 +01:00
$ i + + ;
last if $ i >= 50 ;
}
$ channel = "global channel" if $ channel eq '.*' ;
$ text = "$i most recent $channel submissions:\n\n$text" if $ i > 0 ;
return $ text ;
2010-03-22 08:33:44 +01:00
}
}
2011-01-27 01:49:36 +01:00
my $ user = lc $ args ;
foreach my $ chan ( sort keys % { $ factoids } ) {
next if lc $ chan ne lc $ channel ;
2014-05-18 22:19:30 +02:00
foreach my $ command ( sort { ( $ factoids - > { $ chan } - > { $ b } { last_referenced_on } || 0 ) <=> ( $ factoids - > { $ chan } - > { $ a } { last_referenced_on } || 0 ) } keys % { $ factoids - > { $ chan } } ) {
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ chan } - > { $ command } { ref_user } =~ /\Q$args\E/i ) {
if ( $ user ne lc $ factoids - > { $ chan } - > { $ command } { ref_user } && not $ user =~ /$factoids->{$chan}->{$command}{ref_user}/i ) {
2014-05-18 22:19:30 +02:00
$ user . = " ($factoids->{$chan}->{$command}{ref_user})" ;
2011-01-27 01:49:36 +01:00
}
2014-05-18 22:19:30 +02:00
my $ ago = $ factoids - > { $ chan } - > { $ command } { last_referenced_on } ? ago ( gettimeofday - $ factoids - > { $ chan } - > { $ command } { last_referenced_on } ) : "unknown" ;
2011-01-27 01:49:36 +01:00
$ text . = " $command [$ago]\n" ;
$ i + + ;
last if $ i >= 20 ;
2010-03-22 08:33:44 +01:00
}
}
2011-01-27 01:49:36 +01:00
$ text = "$i factoids last referenced by $user:\n\n$text" if $ i > 0 ;
return $ text ;
2010-03-22 08:33:44 +01:00
}
}
}
sub count {
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2014-05-24 14:01:59 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-03-22 08:33:44 +01:00
my $ i = 0 ;
my $ total = 0 ;
2017-11-18 06:36:41 +01:00
if ( not length $ arguments ) {
return "Usage: count <nick|factoids>" ;
2010-03-22 08:33:44 +01:00
}
2019-05-28 18:19:42 +02:00
$ arguments = ".*" if ( $ arguments =~ /^factoids$/ ) ;
2010-03-22 08:33:44 +01:00
eval {
2014-05-24 14:01:59 +02:00
foreach my $ channel ( keys % { $ factoids } ) {
foreach my $ command ( keys % { $ factoids - > { $ channel } } ) {
next if $ factoids - > { $ channel } - > { $ command } - > { type } ne 'text' ;
2019-06-26 18:34:19 +02:00
$ total + + ;
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ channel } - > { $ command } - > { owner } =~ /^\Q$arguments\E$/i ) {
2014-05-24 14:01:59 +02:00
$ i + + ;
}
2010-03-22 08:33:44 +01:00
}
}
} ;
return "/msg $nick $arguments: $@" if $@ ;
2014-05-24 14:01:59 +02:00
return "I have $i factoids." if $ arguments eq ".*" ;
2010-03-22 08:33:44 +01:00
2019-05-28 18:19:42 +02:00
if ( $ i > 0 ) {
2010-03-22 08:33:44 +01:00
my $ percent = int ( $ i / $ total * 100 ) ;
$ percent = 1 if $ percent == 0 ;
2017-09-02 09:14:13 +02:00
return "/say $arguments has submitted $i factoids out of $total ($percent" . "%)" ;
2010-03-22 08:33:44 +01:00
} else {
2017-09-02 09:14:13 +02:00
return "/say $arguments hasn't submitted any factoids" ;
2010-03-22 08:33:44 +01:00
}
}
2010-06-29 06:33:27 +02:00
sub factfind {
2010-03-22 08:33:44 +01:00
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2014-05-18 22:09:05 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-06-21 16:41:39 +02:00
2019-05-13 09:07:08 +02:00
my $ usage = "Usage: factfind [-channel channel] [-owner regex] [-editby regex] [-refby regex] [-regex] [text]" ;
2019-05-28 18:19:42 +02:00
if ( not defined $ arguments ) {
2019-05-13 09:07:08 +02:00
return $ usage ;
2010-03-26 09:58:25 +01:00
}
2019-05-09 05:48:26 +02:00
my ( $ channel , $ owner , $ refby , $ editby , $ use_regex ) ;
2010-03-26 09:58:25 +01:00
2019-05-09 05:48:26 +02:00
$ channel = $ 1 if $ arguments =~ s/\s*-channel\s+([^\b\s]+)//i ;
$ owner = $ 1 if $ arguments =~ s/\s*-owner\s+([^\b\s]+)//i ;
$ refby = $ 1 if $ arguments =~ s/\s*-refby\s+([^\b\s]+)//i ;
$ editby = $ 1 if $ arguments =~ s/\s*-editby\s+([^\b\s]+)//i ;
$ use_regex = 1 if $ arguments =~ s/\s*-regex\b//i ;
2010-03-26 09:58:25 +01:00
$ owner = '.*' if not defined $ owner ;
2014-06-01 23:31:54 +02:00
$ refby = '.*' if not defined $ refby ;
$ editby = '.*' if not defined $ editby ;
2010-03-26 09:58:25 +01:00
$ arguments =~ s/^\s+// ;
$ arguments =~ s/\s+$// ;
$ arguments =~ s/\s+/ /g ;
2017-11-13 21:00:34 +01:00
$ arguments = substr ( $ arguments , 0 , 30 ) ;
2010-03-26 09:58:25 +01:00
my $ argtype = undef ;
2019-05-28 18:19:42 +02:00
if ( $ owner ne '.*' ) {
2010-03-26 09:58:25 +01:00
$ argtype = "owned by $owner" ;
}
2019-05-28 18:19:42 +02:00
if ( $ refby ne '.*' ) {
if ( not defined $ argtype ) {
2014-06-01 23:31:54 +02:00
$ argtype = "last referenced by $refby" ;
} else {
$ argtype . = " and last referenced by $refby" ;
}
}
2019-05-28 18:19:42 +02:00
if ( $ editby ne '.*' ) {
if ( not defined $ argtype ) {
2014-06-01 23:31:54 +02:00
$ argtype = "last edited by $editby" ;
2010-03-26 09:58:25 +01:00
} else {
2014-06-01 23:31:54 +02:00
$ argtype . = " and last edited by $editby" ;
2010-03-26 09:58:25 +01:00
}
}
2019-05-28 18:19:42 +02:00
if ( $ arguments ne "" ) {
2014-03-04 11:48:08 +01:00
my $ unquoted_args = $ arguments ;
$ unquoted_args =~ s/(?:\\(?!\\))//g ;
$ unquoted_args =~ s/(?:\\\\)/\\/g ;
2019-05-28 18:19:42 +02:00
if ( not defined $ argtype ) {
2014-03-04 11:48:08 +01:00
$ argtype = "with text containing '$unquoted_args'" ;
2010-03-26 09:58:25 +01:00
} else {
2014-03-04 11:48:08 +01:00
$ argtype . = " and with text containing '$unquoted_args'" ;
2010-03-26 09:58:25 +01:00
}
}
2019-05-28 18:19:42 +02:00
if ( not defined $ argtype ) {
2019-05-13 09:07:08 +02:00
return $ usage ;
2010-03-26 09:58:25 +01:00
}
2010-06-29 06:33:27 +02:00
my ( $ text , $ last_trigger , $ last_chan , $ i ) ;
$ last_chan = "" ;
$ i = 0 ;
2010-03-26 09:58:25 +01:00
eval {
2017-09-10 07:37:11 +02:00
use re::engine::RE2 - strict = > 1 ;
2019-05-09 05:48:26 +02:00
my $ regex ;
if ( $ use_regex ) {
$ regex = $ arguments ;
} else {
$ regex = ( $ arguments =~ m/^\w/ ) ? '\b' : '\B' ;
$ regex . = quotemeta $ arguments ;
$ regex . = ( $ arguments =~ m/\w$/ ) ? '\b' : '\B' ;
}
2015-08-16 14:20:57 +02:00
2010-06-29 06:33:27 +02:00
foreach my $ chan ( sort keys % { $ factoids } ) {
2018-05-12 11:52:52 +02:00
next if defined $ channel and $ chan !~ /^$channel$/i ;
2014-05-18 22:19:30 +02:00
foreach my $ trigger ( sort keys % { $ factoids - > { $ chan } } ) {
2019-05-28 18:19:42 +02:00
if ( $ factoids - > { $ chan } - > { $ trigger } - > { type } eq 'text' or $ factoids - > { $ chan } - > { $ trigger } - > { type } eq 'regex' ) {
if ( $ factoids - > { $ chan } - > { $ trigger } - > { owner } =~ /^$owner$/i
2018-05-12 11:52:52 +02:00
&& $ factoids - > { $ chan } - > { $ trigger } - > { ref_user } =~ /^$refby$/i
&& ( exists $ factoids - > { $ chan } - > { $ trigger } - > { edited_by } ? $ factoids - > { $ chan } - > { $ trigger } - > { edited_by } =~ /^$editby$/i : 1 ) ) {
2019-05-28 18:19:42 +02:00
next if ( $ arguments ne "" && $ factoids - > { $ chan } - > { $ trigger } - > { action } !~ /$regex/i && $ trigger !~ /$regex/i ) ;
2010-06-29 06:33:27 +02:00
$ i + + ;
2019-06-26 18:34:19 +02:00
2019-05-28 18:19:42 +02:00
if ( $ chan ne $ last_chan ) {
2010-06-29 08:12:52 +02:00
$ text . = $ chan eq '.*' ? "[global channel] " : "[$chan] " ;
2010-06-29 06:33:27 +02:00
$ last_chan = $ chan ;
}
2018-08-09 02:38:57 +02:00
if ( $ trigger =~ / / ) {
$ text . = "\"$trigger\" " ;
} else {
$ text . = "$trigger " ;
}
2010-06-29 06:33:27 +02:00
$ last_trigger = $ trigger ;
}
2010-03-22 08:33:44 +01:00
}
2010-03-26 09:58:25 +01:00
}
2010-03-22 08:33:44 +01:00
}
2010-03-26 09:58:25 +01:00
} ;
return "/msg $nick $arguments: $@" if $@ ;
2019-05-28 18:19:42 +02:00
if ( $ i == 1 ) {
2010-03-22 08:33:44 +01:00
chop $ text ;
2014-05-24 14:01:59 +02:00
return "Found one factoid submitted for " . ( $ last_chan eq '.*' ? 'global channel' : $ last_chan ) . " " . $ argtype . ": $last_trigger is $factoids->{$last_chan}->{$last_trigger}->{action}" ;
2010-03-22 08:33:44 +01:00
} else {
2014-05-24 14:01:59 +02:00
return "Found $i factoids " . $ argtype . ": $text" unless $ i == 0 ;
2010-06-29 06:33:27 +02:00
2010-06-29 08:12:52 +02:00
my $ chans = ( defined $ channel ? ( $ channel eq '.*' ? 'global channel' : $ channel ) : 'any channels' ) ;
2010-06-29 06:33:27 +02:00
return "No factoids " . $ argtype . " submitted for $chans" ;
2010-03-22 08:33:44 +01:00
}
}
2010-06-23 04:15:13 +02:00
sub factchange {
2010-03-22 08:33:44 +01:00
my $ self = shift ;
2019-06-09 22:57:52 +02:00
my ( $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2014-05-18 22:09:05 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-06-23 04:15:13 +02:00
my ( $ channel , $ trigger , $ keyword , $ delim , $ tochange , $ changeto , $ modifier ) ;
2010-03-22 08:33:44 +01:00
2019-06-09 22:57:52 +02:00
$ stuff - > { preserve_whitespace } = 1 ;
2017-08-09 06:18:00 +02:00
my $ needs_disambig ;
2019-06-09 22:57:52 +02:00
if ( length $ arguments ) {
my $ args = $ stuff - > { arglist } ;
my $ sub ;
my $ arg_count = $ self - > { pbot } - > { interpreter } - > arglist_size ( $ args ) ;
if ( $ arg_count >= 3 and ( $ args - > [ 0 ] =~ m/^#/ or $ args - > [ 0 ] eq '.*' or lc $ args - > [ 0 ] eq 'global' ) and ( $ args - > [ 2 ] =~ m/^s([[:punct:]])/ ) ) {
$ delim = $ 1 ;
$ channel = $ args - > [ 0 ] ;
$ keyword = $ args - > [ 1 ] ;
( $ sub ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ args , 1 , 2 ) ;
$ needs_disambig = 0 ;
} elsif ( $ arg_count >= 2 and $ args - > [ 1 ] =~ m/^s([[:punct:]])/ ) {
$ delim = $ 1 ;
$ keyword = $ args - > [ 0 ] ;
2017-08-02 06:33:57 +02:00
$ channel = $ from ;
2019-06-09 22:57:52 +02:00
( $ sub ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ args , 1 , 1 ) ;
2017-08-09 06:18:00 +02:00
$ needs_disambig = 1 ;
2010-03-22 08:33:44 +01:00
}
2019-06-09 22:57:52 +02:00
2015-09-30 18:50:37 +02:00
$ delim = quotemeta $ delim ;
2017-01-30 03:01:26 +01:00
2019-06-09 22:57:52 +02:00
if ( $ sub =~ /^s$delim(.*?)$delim(.*)$delim(.*)$/ ) {
2019-06-26 18:34:19 +02:00
$ tochange = $ 1 ;
2010-03-22 08:33:44 +01:00
$ changeto = $ 2 ;
$ modifier = $ 3 ;
2019-06-10 00:36:48 +02:00
} elsif ( $ sub =~ /^s$delim(.*?)$delim(.*)$/ ) {
$ tochange = $ 1 ;
$ changeto = $ 2 ;
$ modifier = '' ;
2010-03-22 08:33:44 +01:00
}
}
2017-08-02 06:33:57 +02:00
if ( not defined $ channel or not defined $ changeto ) {
return "Usage: factchange [channel] <keyword> s/<pattern>/<replacement>/" ;
2010-03-22 08:33:44 +01:00
}
2017-08-09 06:18:00 +02:00
my ( $ from_trigger , $ from_chan ) = ( $ keyword , $ channel ) ;
2019-06-10 01:33:27 +02:00
my @ factoids = $ self - > { pbot } - > { factoids } - > find_factoid ( $ from_chan , $ keyword , exact_trigger = > 1 ) ;
2017-08-09 06:18:00 +02:00
if ( not @ factoids or not $ factoids [ 0 ] ) {
$ from_chan = 'global channel' if $ from_chan eq '.*' ;
2017-09-02 09:14:13 +02:00
return "/say $keyword not found in $from_chan" ;
2017-08-09 06:18:00 +02:00
}
if ( @ factoids > 1 ) {
if ( not grep { $ _ - > [ 0 ] eq $ from_chan } @ factoids ) {
2017-09-02 09:14:13 +02:00
return "/say $from_trigger found in multiple channels: " . ( join ', ' , sort map { $ _ - > [ 0 ] eq '.*' ? 'global' : $ _ - > [ 0 ] } @ factoids ) . "; use `factchange <channel> $from_trigger` to disambiguate." ;
2017-08-09 06:18:00 +02:00
} else {
foreach my $ factoid ( @ factoids ) {
if ( $ factoid - > [ 0 ] eq $ from_chan ) {
( $ channel , $ trigger ) = ( $ factoid - > [ 0 ] , $ factoid - > [ 1 ] ) ;
last ;
}
}
}
} else {
( $ channel , $ trigger ) = ( $ factoids [ 0 ] - > [ 0 ] , $ factoids [ 0 ] - > [ 1 ] ) ;
}
2010-03-22 08:33:44 +01:00
2017-08-02 06:33:57 +02:00
if ( not defined $ trigger ) {
2017-09-02 09:14:13 +02:00
return "/say $keyword not found in channel $from_chan." ;
2010-06-20 08:16:48 +02:00
}
2010-03-22 08:33:44 +01:00
2017-08-09 06:18:00 +02:00
$ from_chan = '.*' if $ from_chan eq 'global' ;
if ( $ channel =~ /^#/ and $ from_chan =~ /^#/ and $ channel ne $ from_chan ) {
2017-09-02 09:14:13 +02:00
return "/say $trigger belongs to $channel, but this is $from_chan. Please switch to $channel or use /msg to change this factoid." ;
2017-08-02 06:33:57 +02:00
}
2016-11-17 04:07:01 +01:00
my $ admininfo = $ self - > { pbot } - > { admins } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
if ( $ factoids - > { $ channel } - > { $ trigger } - > { 'locked' } ) {
2017-09-02 09:14:13 +02:00
return "/say $trigger is locked and cannot be changed." if not defined $ admininfo ;
2016-11-17 04:07:01 +01:00
if ( exists $ factoids - > { $ channel } - > { $ trigger } - > { 'effective-level' }
and $ admininfo - > { level } < $ factoids - > { $ channel } - > { $ trigger } - > { 'effective-level' } ) {
2017-09-02 09:14:13 +02:00
return "/say $trigger is locked with an effective-level higher than your level and cannot be changed." ;
2016-11-17 04:07:01 +01:00
}
2013-09-13 23:48:19 +02:00
}
2010-03-22 08:33:44 +01:00
my $ ret = eval {
2014-04-29 19:00:51 +02:00
use re::engine::RE2 - strict = > 1 ;
2017-08-25 00:18:41 +02:00
my $ action = $ factoids - > { $ channel } - > { $ trigger } - > { action } ;
2017-09-29 21:07:50 +02:00
my $ changed ;
2018-02-19 22:45:24 +01:00
if ( $ modifier eq 'gi' or $ modifier eq 'ig' or $ modifier eq 'g' ) {
my @ chars = ( "A" .. "Z" , "a" .. "z" , "0" .. "9" ) ;
my $ magic = '' ;
$ magic . = $ chars [ rand @ chars ] for 1 .. ( 10 * rand ) + 10 ;
my $ insensitive = index ( $ modifier , 'i' ) + 1 ;
my $ count = 0 ;
my $ max = 50 ;
while ( 1 ) {
if ( $ count == 0 ) {
if ( $ insensitive ) {
$ changed = $ action =~ s | $ tochange | $ changeto $ magic | i ;
} else {
$ changed = $ action =~ s | $ tochange | $ changeto $ magic | ;
}
} else {
if ( $ insensitive ) {
$ changed = $ action =~ s | $ tochange | $ 1 $ changeto $ magic | i ;
} else {
$ changed = $ action =~ s | $ tochange | $ 1 $ changeto $ magic | ;
}
}
if ( $ changed ) {
$ count + + ;
if ( $ count == $ max ) {
$ action =~ s/$magic// ;
last ;
}
$ tochange = "$magic(.*?)$tochange" if $ count == 1 ;
} else {
$ changed = $ count ;
$ action =~ s/$magic// if $ changed ;
last ;
}
}
2017-09-29 21:07:50 +02:00
} elsif ( $ modifier eq 'i' ) {
$ changed = $ action =~ s | $ tochange | $ changeto | i ;
} else {
$ changed = $ action =~ s | $ tochange | $ changeto | ;
}
if ( not $ changed ) {
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "($from) $nick!$user\@$host: failed to change '$trigger' 's$delim$tochange$delim$changeto$delim\n" ) ;
2014-06-01 23:31:54 +02:00
return "Change $trigger failed." ;
2010-03-22 08:33:44 +01:00
} else {
2017-09-29 21:07:50 +02:00
if ( length $ action > 8000 and not defined $ admininfo ) {
2017-08-25 00:18:41 +02:00
return "Change $trigger failed; result is too long." ;
}
if ( not length $ action ) {
return "Change $trigger failed; factoids cannot be empty." ;
}
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "($from) $nick!$user\@$host: changed '$trigger' 's/$tochange/$changeto/\n" ) ;
2017-08-25 00:18:41 +02:00
$ factoids - > { $ channel } - > { $ trigger } - > { action } = $ action ;
2014-05-18 22:19:30 +02:00
$ factoids - > { $ channel } - > { $ trigger } - > { edited_by } = "$nick!$user\@$host" ;
$ factoids - > { $ channel } - > { $ trigger } - > { edited_on } = gettimeofday ;
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { factoids } - > save_factoids ( ) ;
2015-12-22 17:12:59 +01:00
$ self - > log_factoid ( $ channel , $ trigger , "$nick!$user\@$host" , "changed to $factoids->{$channel}->{$trigger}->{action}" ) ;
2014-05-18 22:19:30 +02:00
return "Changed: $trigger is " . $ factoids - > { $ channel } - > { $ trigger } - > { action } ;
2010-03-22 08:33:44 +01:00
}
} ;
2010-06-20 08:16:48 +02:00
return "/msg $nick Change $trigger: $@" if $@ ;
2010-03-22 08:33:44 +01:00
return $ ret ;
}
sub load_module {
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2014-05-24 14:01:59 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-03-22 08:33:44 +01:00
my ( $ keyword , $ module ) = $ arguments =~ /^(.*?)\s+(.*)$/ if defined $ arguments ;
2019-05-28 18:19:42 +02:00
if ( not defined $ module ) {
2014-06-01 23:31:54 +02:00
return "Usage: load <keyword> <module>" ;
2010-03-22 08:33:44 +01:00
}
2019-05-28 18:19:42 +02:00
if ( not exists ( $ factoids - > { '.*' } - > { $ keyword } ) ) {
2014-05-24 14:01:59 +02:00
$ self - > { pbot } - > { factoids } - > add_factoid ( 'module' , '.*' , "$nick!$user\@$host" , $ keyword , $ module ) ;
$ factoids - > { '.*' } - > { $ keyword } - > { add_nick } = 1 ;
2014-07-11 14:56:17 +02:00
$ factoids - > { '.*' } - > { $ keyword } - > { nooverride } = 1 ;
2014-05-24 14:01:59 +02:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host loaded module $keyword => $module\n" ) ;
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { factoids } - > save_factoids ( ) ;
2014-06-01 23:31:54 +02:00
return "Loaded module $keyword => $module" ;
2010-03-22 08:33:44 +01:00
} else {
2014-06-01 23:31:54 +02:00
return "There is already a keyword named $keyword." ;
2010-03-22 08:33:44 +01:00
}
}
sub unload_module {
my $ self = shift ;
my ( $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2014-05-24 14:01:59 +02:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } - > hash ;
2010-03-22 08:33:44 +01:00
2019-05-28 18:19:42 +02:00
if ( not defined $ arguments ) {
2014-06-01 23:31:54 +02:00
return "Usage: unload <keyword>" ;
2019-05-28 18:19:42 +02:00
} elsif ( not exists $ factoids - > { '.*' } - > { $ arguments } ) {
2017-09-02 09:14:13 +02:00
return "/say $arguments not found." ;
2019-05-28 18:19:42 +02:00
} elsif ( $ factoids - > { '.*' } - > { $ arguments } { type } ne 'module' ) {
2017-09-02 09:14:13 +02:00
return "/say $arguments is not a module." ;
2010-03-22 08:33:44 +01:00
} else {
2014-05-24 14:01:59 +02:00
delete $ factoids - > { '.*' } - > { $ arguments } ;
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { factoids } - > save_factoids ( ) ;
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host unloaded module $arguments\n" ) ;
2017-09-02 09:14:13 +02:00
return "/say $arguments unloaded." ;
2019-06-26 18:34:19 +02:00
}
2010-03-22 08:33:44 +01:00
}
1 ;