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
#
2020-01-11 03:59:15 +01:00
# Purpose: Factoid command subroutines.
2010-03-22 08:33:44 +01:00
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 ;
2020-02-08 20:04:13 +01:00
use parent 'PBot::Class' ;
2010-03-22 08:33:44 +01:00
2020-02-08 20:04:13 +01:00
use warnings ; use strict ;
2019-07-11 03:40:53 +02:00
use feature 'unicode_strings' ;
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 ;
2020-01-20 05:00:01 +01:00
use LWP::UserAgent ;
2019-07-01 07:19:04 +02:00
use JSON ;
2010-03-22 08:33:44 +01:00
2017-09-05 04:07:10 +02:00
use PBot::Utils::SafeFilename ;
2020-02-04 02:19:04 +01:00
our % factoid_metadata_capabilities = (
created_on = > 'botowner' ,
enabled = > 'chanop' ,
last_referenced_in = > 'botowner' ,
last_referenced_on = > 'botowner' ,
modulelauncher_subpattern = > 'botowner' ,
owner = > 'botowner' ,
rate_limit = > 'chanop' ,
ref_count = > 'botowner' ,
ref_user = > 'botowner' ,
type = > 'botowner' ,
edited_by = > 'botowner' ,
edited_on = > 'botowner' ,
locked = > 'chanop' ,
add_nick = > 'chanop' ,
nooverride = > 'chanop' ,
'cap-override' = > 'botowner' ,
'persist-key' = > 'admin' ,
# all others are allowed to be factset by anybody
2011-01-25 23:40:22 +01:00
) ;
2010-03-22 08:33:44 +01:00
sub initialize {
my ( $ self , % conf ) = @ _ ;
2020-01-15 03:10:53 +01:00
$ self - > { pbot } - > { registry } - > add_default ( 'text' , 'general' , 'module_repo' , $ conf { module_repo } // 'https://github.com/pragma-/pbot/blob/master/modules/' ) ;
2020-02-06 10:07:44 +01:00
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factadd ( @ _ ) } , "learn" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factadd ( @ _ ) } , "factadd" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factrem ( @ _ ) } , "forget" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factrem ( @ _ ) } , "factrem" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factshow ( @ _ ) } , "factshow" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factinfo ( @ _ ) } , "factinfo" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factlog ( @ _ ) } , "factlog" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factundo ( @ _ ) } , "factundo" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factredo ( @ _ ) } , "factredo" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factset ( @ _ ) } , "factset" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factunset ( @ _ ) } , "factunset" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factchange ( @ _ ) } , "factchange" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factalias ( @ _ ) } , "factalias" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factmove ( @ _ ) } , "factmove" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > call_factoid ( @ _ ) } , "fact" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > factfind ( @ _ ) } , "factfind" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > top20 ( @ _ ) } , "top20" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > histogram ( @ _ ) } , "histogram" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ 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.
2020-02-06 10:07:44 +01:00
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > add_regex ( @ _ ) } , "regex" , 1 ) ;
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 ) = @ _ ;
2020-02-02 07:19:53 +01:00
my ( $ chan , $ keyword , $ args ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 3 , 0 , 1 ) ;
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 ) {
2020-01-29 17:34:30 +01:00
return "No such factoid $keyword exists for $chan" ;
2010-06-21 15:54:27 +02:00
}
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
2020-01-15 03:10:53 +01:00
$ channel = lc $ channel ;
$ trigger = lc $ trigger ;
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 ;
2019-07-01 07:19:04 +02:00
my $ h = { ts = > $ now , hm = > $ hostmask , msg = > $ msg } ;
my $ json = encode_json $ h ;
print $ fh "$json\n" ;
2015-12-13 22:58:01 +01:00
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 = > []
} ;
}
2020-02-13 22:31:36 +01:00
my $ max_undos = $ self - > { pbot } - > { registry } - > get_value ( 'factoids' , 'max_undos' ) // 20 ;
if ( @ { $ undos - > { list } } > $ max_undos ) {
2017-08-26 16:03:01 +02:00
shift @ { $ undos - > { list } } ;
$ undos - > { idx } - - ;
}
if ( $ undos - > { idx } > - 1 and @ { $ undos - > { list } } > $ undos - > { idx } + 1 ) {
splice @ { $ undos - > { list } } , $ undos - > { idx } + 1 ;
}
2020-02-13 22:31:36 +01:00
push @ { $ undos - > { list } } , $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger ) ;
2017-08-26 16:03:01 +02:00
$ 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 ) ;
2020-02-02 07:19:53 +01:00
my ( $ from_chan , $ from_trigger , $ remaining_args ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 3 , 0 , 1 ) ;
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 ;
2020-02-02 07:19:53 +01:00
( undef , $ remaining_args ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 2 , 0 , 1 ) ;
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 ) {
2020-02-13 22:31:36 +01:00
my $ channel_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
return "/say $trigger_name belongs to $channel_name, not $from_chan. Please switch to or explicitly specify $channel_name." ;
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
2020-02-13 22:31:36 +01:00
return "No change." if not keys % diff ;
2017-08-29 08:14:32 +02:00
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-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 ;
2020-02-13 22:31:36 +01:00
my $ path = $ self - > { pbot } - > { registry } - > get_data ( '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
2020-02-13 22:31:36 +01:00
my $ channel_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
2017-08-26 16:03:01 +02:00
if ( not $ undos ) {
2020-01-15 03:10:53 +01:00
return "There are no undos available for [$channel_name] $trigger_name." ;
2017-08-26 16:03:01 +02:00
}
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
}
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
2020-02-04 02:19:04 +01:00
my $ userinfo = $ self - > { pbot } - > { users } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ channel , $ trigger , 'locked' ) ) {
2020-02-04 02:19:04 +01:00
return "/say $trigger_name is locked and cannot be reverted." if not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'admin' ) ;
2017-09-05 11:18:02 +02:00
2020-02-13 22:31:36 +01:00
if ( $ factoids - > exists ( $ channel , $ trigger , 'cap-override' ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'botowner' ) ) {
2020-02-04 02:19:04 +01:00
return "/say $trigger_name is locked with a cap-override and cannot be reverted. Unlock the factoid first." ;
2017-09-05 11:18:02 +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 ) {
2020-01-15 03:10:53 +01:00
return "There is only one revision available for [$channel_name] $trigger_name." ;
2017-12-12 12:38:45 +01:00
} else {
2020-01-15 03:10:53 +01:00
return "There are " . @ { $ undos - > { list } } . " revisions available for [$channel_name] $trigger_name." ;
2017-12-12 12:38:45 +01:00
}
}
if ( $ goto_revision == $ undos - > { idx } + 1 ) {
2020-01-15 03:10:53 +01:00
return "[$channel_name] $trigger_name is already at revision $goto_revision." ;
2017-12-12 12:38:45 +01:00
}
$ 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 ) {
2020-01-15 03:10:53 +01:00
return "There are no more undos remaining for [$channel_name] $trigger_name." if not $ undos - > { idx } ;
2017-12-12 12:38:45 +01:00
$ 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
2020-02-13 22:31:36 +01:00
$ self - > { pbot } - > { factoids } - > { factoids } - > add ( $ channel , $ trigger , $ undos - > { list } - > [ $ undos - > { idx } ] , 0 , 1 ) ;
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 ) ;
2020-01-15 03:10:53 +01:00
return "[$channel_name] $trigger_name 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 ;
2020-02-13 22:31:36 +01:00
my $ channel_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
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 ) {
2020-01-15 03:10:53 +01:00
return "There are no redos available for [$channel_name] $trigger_name." ;
2017-08-26 16:03:01 +02:00
}
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
}
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
2020-02-04 02:19:04 +01:00
my $ userinfo = $ self - > { pbot } - > { users } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ channel , $ trigger , 'locked' ) ) {
2020-02-04 02:19:04 +01:00
return "/say $trigger_name is locked and cannot be reverted." if not defined $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'admin' ) ;
2017-09-05 11:18:02 +02:00
2020-02-13 22:31:36 +01:00
if ( $ factoids - > exists ( $ channel , $ trigger , 'cap-override' ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'botowner' ) ) {
2020-02-04 02:19:04 +01:00
return "/say $trigger_name is locked with a cap-override and cannot be reverted. Unlock the factoid first." ;
2017-09-05 11:18:02 +02:00
}
}
2017-12-12 12:38:45 +01:00
if ( not defined $ goto_revision and $ undos - > { idx } + 1 == @ { $ undos - > { list } } ) {
2020-01-15 03:10:53 +01:00
return "There are no more redos remaining for [$channel_name] $trigger_name." ;
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 ) {
2020-01-15 03:10:53 +01:00
return "There is only one revision available for [$channel_name] $trigger_name." ;
2017-12-12 12:38:45 +01:00
} else {
2020-01-15 03:10:53 +01:00
return "There are " . @ { $ undos - > { list } } . " revisions available for [$channel_name] $trigger_name." ;
2017-12-12 12:38:45 +01:00
}
}
if ( $ goto_revision == $ undos - > { idx } + 1 ) {
2020-01-15 03:10:53 +01:00
return "[$channel_name] $trigger_name is already at revision $goto_revision." ;
2017-12-12 12:38:45 +01:00
}
$ 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
2020-02-13 22:31:36 +01:00
$ self - > { pbot } - > { factoids } - > { factoids } - > add ( $ channel , $ trigger , $ undos - > { list } - > [ $ undos - > { idx } ] , 0 , 1 ) ;
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 ) ;
2020-01-15 03:10:53 +01:00
return "[$channel_name] $trigger_name 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
2020-02-13 22:31:36 +01:00
my $ trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
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
2020-02-04 02:19:04 +01:00
my $ userinfo ;
2015-06-06 07:26:02 +02:00
if ( defined $ owner_channel ) {
2020-02-04 02:19:04 +01:00
$ userinfo = $ self - > { pbot } - > { users } - > loggedin ( $ owner_channel , "$nick!$user\@$host" ) ;
2015-06-06 07:26:02 +02:00
} else {
2020-02-04 02:19:04 +01:00
$ userinfo = $ self - > { pbot } - > { users } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
2011-01-25 23:40:22 +01:00
}
2020-02-04 02:19:04 +01:00
my $ meta_cap ;
2019-05-28 18:19:42 +02:00
if ( defined $ key ) {
2020-02-04 02:19:04 +01:00
if ( defined $ factoid_metadata_capabilities { $ key } ) {
$ meta_cap = $ factoid_metadata_capabilities { $ key } ;
2011-01-25 23:40:22 +01:00
}
2020-02-04 02:19:04 +01:00
if ( defined $ meta_cap ) {
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , $ meta_cap ) ) {
return "Your user account must have the $meta_cap capability to set $key." ;
2011-01-25 23:40:22 +01:00
}
}
2016-11-17 04:07:01 +01:00
2020-02-13 22:31:36 +01:00
if ( defined $ value and ! $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'admin' ) and $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , 'locked' ) ) {
2020-01-15 03:10:53 +01:00
return "/say $trigger_name is locked; unlock before setting." ;
2019-05-28 04:40:24 +02:00
}
2020-02-04 02:19:04 +01:00
if ( lc $ key eq 'cap-override' and defined $ value ) {
if ( not $ self - > { pbot } - > { capabilities } - > exists ( $ value ) ) {
return "No such capability $value." ;
2016-11-17 04:07:01 +01:00
}
$ self - > { pbot } - > { factoids } - > { factoids } - > set ( $ channel , $ trigger , 'locked' , '1' ) ;
}
2020-02-13 22:31:36 +01:00
if ( lc $ key eq 'locked' and $ self - > { pbot } - > { factoids } - > { factoids } - > exists ( $ channel , $ trigger , 'cap-override' ) ) {
2020-02-04 02:19:04 +01:00
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'botowner' ) ) {
return "/say $trigger_name has a cap-override and cannot be unlocked until the override is removed." ;
2016-11-17 04:07:01 +01:00
}
}
2011-01-25 23:40:22 +01:00
}
2019-05-08 22:06:30 +02:00
if ( defined $ owner_channel ) {
2020-02-13 22:31:36 +01:00
my $ factoid = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ owner_channel , $ owner_trigger ) ;
2011-01-25 23:40:22 +01:00
2019-08-22 18:49:14 +02:00
my $ owner ;
my $ mask ;
if ( $ factoid - > { 'locked' } ) {
# check owner against full hostmask for locked factoids
$ owner = $ factoid - > { 'owner' } ;
$ mask = "$nick!$user\@$host" ;
} else {
# otherwise just the nick
( $ owner ) = $ factoid - > { 'owner' } =~ m/([^!]+)/ ;
$ mask = $ nick ;
}
2013-07-31 15:29:37 +02:00
2020-02-04 02:19:04 +01:00
if ( ( defined $ value and $ key ne 'action' and $ key ne 'action_with_args' ) and lc $ mask ne lc $ owner and not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'admin' ) ) {
2020-01-15 03:10:53 +01:00
return "You are not the owner of $trigger_name." ;
2011-01-25 23:40:22 +01:00
}
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 ) = @ _ ;
my $ usage = 'Usage: factunset [channel] <factoid> <key>' ;
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 ) ;
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 ) ;
2020-02-04 02:19:04 +01:00
my $ userinfo ;
2015-06-06 07:26:02 +02:00
if ( defined $ owner_channel ) {
2020-02-04 02:19:04 +01:00
$ userinfo = $ self - > { pbot } - > { users } - > loggedin ( $ owner_channel , "$nick!$user\@$host" ) ;
2015-06-06 07:26:02 +02:00
} else {
2020-02-04 02:19:04 +01:00
$ userinfo = $ self - > { pbot } - > { users } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
2015-06-06 07:26:02 +02:00
}
2011-01-25 23:40:22 +01:00
2020-02-04 02:19:04 +01:00
my $ meta_cap ;
if ( exists $ factoid_metadata_capabilities { $ key } ) {
$ meta_cap = $ factoid_metadata_capabilities { $ key } ;
2011-01-25 23:40:22 +01:00
}
2020-02-04 02:19:04 +01:00
if ( defined $ meta_cap ) {
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , $ meta_cap ) ) {
return "Your user account must have the $meta_cap capability to unset $key." ;
2011-01-25 23:40:22 +01:00
}
}
2020-02-13 22:31:36 +01:00
if ( $ self - > { pbot } - > { factoids } - > { factoids } - > exists ( $ channel , $ trigger , 'cap-override' ) ) {
2016-11-17 04:07:01 +01:00
if ( lc $ key eq 'locked' ) {
2020-02-04 02:19:04 +01:00
if ( $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'botowner' ) ) {
2020-02-13 22:31:36 +01:00
$ self - > { pbot } - > { factoids } - > { factoids } - > unset ( $ channel , $ trigger , 'cap-override' , 1 ) ;
2016-11-17 04:07:01 +01:00
} else {
2020-02-04 02:19:04 +01:00
return "You cannot unlock this factoid because it has a cap-override. Remove the override first." ;
2016-11-17 04:07:01 +01:00
}
}
}
2020-02-13 22:31:36 +01:00
my $ channel_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
2020-02-13 22:31:36 +01:00
my $ oldvalue ;
2019-05-28 18:19:42 +02:00
if ( defined $ owner_channel ) {
2020-02-13 22:31:36 +01:00
my $ factoid = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ owner_channel , $ owner_trigger ) ;
2013-07-31 15:29:37 +02:00
my ( $ owner ) = $ factoid - > { 'owner' } =~ m/([^!]+)/ ;
2020-02-04 02:19:04 +01:00
if ( $ key ne 'action_with_args' and lc $ nick ne lc $ owner and not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'admin' ) ) {
2020-01-15 03:10:53 +01:00
return "You are not the owner of $trigger_name." ;
2011-01-25 23:40:22 +01:00
}
2020-02-13 22:31:36 +01:00
$ oldvalue = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , $ key ) ;
2011-01-25 23:40:22 +01:00
}
2020-01-15 03:10:53 +01:00
return "[$channel_name] $trigger_name: 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
}
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 ) ;
2014-05-23 14:42:23 +02:00
my $ usage = "Usage: factmove <source channel> <source factoid> <target channel/factoid> [target factoid]" ;
2020-02-13 22:31:36 +01:00
return $ usage if not defined $ target_channel ;
2014-05-23 14:42:23 +02:00
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" ;
}
2020-02-13 22:31:36 +01:00
my $ source_channel_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ found_src_channel , '_name' ) ;
my $ source_trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ found_src_channel , $ found_source , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ source_channel_name = 'global' if $ source_channel_name eq '.*' ;
$ source_trigger_name = "\"$source_trigger_name\"" if $ source_trigger_name =~ / / ;
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
my ( $ owner ) = $ factoids - > get_data ( $ found_src_channel , $ found_source , 'owner' ) =~ m/([^!]+)/ ;
2014-05-23 14:42:23 +02:00
2020-01-25 21:28:05 +01:00
if ( ( lc $ nick ne lc $ owner ) and ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ 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 ) ;
2020-01-15 03:10:53 +01:00
return "You are not the owner of $source_trigger_name for $source_channel_name." ;
2014-05-23 14:42:23 +02:00
}
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ found_src_channel , $ found_source , 'locked' ) ) {
2020-01-15 03:10:53 +01:00
return "/say $source_trigger_name 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 ) {
2020-02-13 22:31:36 +01:00
my $ target_channel_name = $ factoids - > get_data ( $ found_target_channel , '_name' ) ;
my $ target_trigger_name = $ factoids - > get_data ( $ found_target_channel , $ found_target , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ target_channel_name = 'global' if $ target_channel_name eq '.*' ;
$ target_trigger_name = "\"$target_trigger_name\"" if $ target_trigger_name =~ / / ;
return "Target factoid $target_trigger_name already exists in channel $target_channel_name." ;
2014-05-23 14:42:23 +02:00
}
2019-06-10 01:33:27 +02:00
my ( $ overchannel , $ overtrigger ) = $ self - > { pbot } - > { factoids } - > find_factoid ( '.*' , $ target , exact_channel = > 1 , exact_trigger = > 1 ) ;
2020-02-13 22:31:36 +01:00
if ( defined $ overtrigger and $ factoids - > get_data ( '.*' , $ overtrigger , 'nooverride' ) ) {
my $ override_channel_name = $ factoids - > get_data ( $ overchannel , '_name' ) ;
my $ override_trigger_name = $ factoids - > get_data ( $ overchannel , $ overtrigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ override_channel_name = 'global' if $ override_channel_name eq '.*' ;
$ override_trigger_name = "\"$override_trigger_name\"" if $ override_trigger_name =~ / / ;
2017-11-30 00:55:53 +01:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host attempt to override $target\n" ) ;
2020-01-15 03:10:53 +01:00
return "/say $override_trigger_name already exists for the global channel and cannot be overridden for " . ( $ target_channel eq '.*' ? 'the global channel' : $ target_channel ) . "." ;
2017-11-30 00:55:53 +01:00
}
2017-12-03 00:05:56 +01:00
if ( $ self - > { pbot } - > { commands } - > exists ( $ target ) ) {
return "/say $target already exists as a built-in command." ;
}
2017-08-24 04:18:14 +02:00
$ target_channel = '.*' if $ target_channel !~ /^#/ ;
2014-05-23 14:42:23 +02:00
2020-02-13 22:31:36 +01:00
my $ data = $ factoids - > get_data ( $ found_src_channel , $ found_source ) ;
$ factoids - > remove ( $ found_src_channel , $ found_source , undef , 1 ) ;
$ factoids - > add ( $ target_channel , $ target , $ data , 0 , 1 ) ;
2014-05-23 14:42:23 +02:00
2015-02-16 05:17:36 +01:00
$ found_src_channel = 'global' if $ found_src_channel eq '.*' ;
$ target_channel = 'global' if $ target_channel eq '.*' ;
2020-01-15 03:10:53 +01:00
if ( $ src_channel eq lc $ target_channel ) {
$ self - > log_factoid ( $ found_src_channel , $ found_source , "$nick!$user\@$host" , "renamed from $source_trigger_name to $target" ) ;
$ self - > log_factoid ( $ target_channel , $ target , "$nick!$user\@$host" , "renamed from $source_trigger_name to $target" ) ;
return "[$source_channel_name] $source_trigger_name renamed to $target" ;
2014-05-23 14:42:23 +02:00
} else {
2020-01-15 03:10:53 +01:00
$ self - > log_factoid ( $ found_src_channel , $ found_source , "$nick!$user\@$host" , "moved from $source_channel_name/$source_trigger_name to $target_channel/$target" ) ;
$ self - > log_factoid ( $ target_channel , $ target , "$nick!$user\@$host" , "moved from $source_channel_name/$source_trigger_name to $target_channel/$target" ) ;
return "[$source_channel_name] $source_trigger_name moved to [$target_channel] $target" ;
2014-05-23 14:42:23 +02:00
}
}
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 ) = @ _ ;
2020-02-02 07:19:53 +01:00
my ( $ chan , $ alias , $ command ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 3 , 0 , 1 ) ;
2017-12-12 00:24:37 +01:00
if ( defined $ chan and not ( $ chan eq '.*' or $ chan =~ m/^#/ ) ) {
2020-01-15 03:10:53 +01:00
# $chan doesn't look like a channel, so shift everything to the right
2017-12-12 00:24:37 +01:00
# 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 !~ /^#/ ;
2020-02-13 22:31:36 +01:00
return "Usage: factalias [channel] <keyword> <command>" if not length $ alias or not length $ 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-05-28 18:19:42 +02:00
if ( defined $ alias_trigger ) {
2020-02-13 22:31:36 +01:00
my $ alias_channel_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , '_name' ) ;
my $ alias_trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ alias_trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ alias_channel_name = 'global' if $ alias_channel_name eq '.*' ;
$ alias_trigger_name = "\"$alias_trigger_name\"" if $ alias_trigger_name =~ / / ;
return "$alias_trigger_name already exists for $alias_channel_name." ;
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 ) ;
2020-02-13 22:31:36 +01:00
if ( defined $ overtrigger and $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( '.*' , $ overtrigger , 'nooverride' ) ) {
my $ override_trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ overchannel , $ overtrigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ override_trigger_name = "\"$override_trigger_name\"" if $ override_trigger_name =~ / / ;
return "/say $override_trigger_name already exists for the global channel and cannot be overridden for " . ( $ chan eq '.*' ? 'the global channel' : $ chan ) . "." ;
2017-11-30 00:55:53 +01:00
}
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" ) ;
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host [$chan] aliased $alias => $command\n" ) ;
$ self - > { pbot } - > { factoids } - > save_factoids ( ) ;
2020-01-15 03:10:53 +01: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 ) = @ _ ;
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
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 = "" ;
2020-02-13 22:31:36 +01:00
foreach my $ trigger ( sort $ factoids - > get_keys ( $ from ) ) {
if ( $ factoids - > get_data ( $ 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
2019-07-01 02:54:08 +02:00
my ( $ url ) = $ self - > { pbot } - > { interpreter } - > split_args ( \ @ arglist , 1 ) ;
2020-01-15 03:10:53 +01:00
# FIXME: move this to registry
2019-07-01 02:54:08 +02:00
if ( $ url !~ m/^https?:\/\/(?:sprunge.us|ix.io)\/\w+$/ ) {
2019-07-02 06:36:08 +02:00
return "Invalid URL: acceptable URLs are: http://sprunge.us, http://ix.io" ;
2019-07-01 02:54:08 +02:00
}
2019-07-01 02:45:05 +02:00
# 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
2020-02-02 07:19:53 +01:00
( $ text ) = $ self - > { pbot } - > { interpreter } - > split_args ( \ @ arglist , 1 , 0 , 1 ) ;
2019-07-01 02:45:05 +02:00
}
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-07-01 02:54:08 +02:00
return "Usage: factadd [-f] [channel] <keyword> (<factoid> | -url <paste site>); -f to force overwrite; -url to download from paste site" ;
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 ) {
2020-02-13 22:31:36 +01:00
my $ channel_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
2019-06-03 07:34:17 +02:00
if ( not $ force ) {
2020-01-15 03:10:53 +01:00
return "/say $trigger_name already exists for $channel_name." ;
2019-06-03 07:34:17 +02:00
} else {
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
2019-06-03 07:34:17 +02:00
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ channel , $ trigger , 'locked' ) ) {
2020-01-15 03:10:53 +01:00
return "/say $trigger_name is locked; unlock before overwriting." ;
2019-06-03 07:34:17 +02:00
}
2020-02-13 22:31:36 +01:00
my ( $ owner ) = $ factoids - > get_data ( $ channel , $ trigger , 'owner' ) =~ m/([^!]+)/ ;
2020-01-25 21:28:05 +01:00
if ( ( lc $ nick ne lc $ owner ) and ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ channel , "$nick!$user\@$host" ) ) ) {
2020-01-15 03:10:53 +01:00
return "You are not the owner of $trigger_name for $channel_name; cannot force 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 ) ;
2020-02-13 22:31:36 +01:00
if ( defined $ trigger and $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( '.*' , $ trigger , 'nooverride' ) ) {
my $ trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
return "/say $trigger_name 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 ) ;
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 ) = @ _ ;
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
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
2020-02-13 22:31:36 +01:00
my $ channel_name = $ factoids - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ factoids - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
2018-08-09 02:58:53 +02:00
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ channel , $ trigger , 'type' ) eq 'module' ) {
2020-01-15 03:10:53 +01:00
return "/say $trigger_name 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 ) {
2020-01-15 03:10:53 +01:00
return "/say $trigger_name belongs to $channel_name, but this is $from_chan. Please switch to $channel_name or use /msg to remove this factoid." ;
2017-08-02 06:33:57 +02:00
}
2020-02-13 22:31:36 +01:00
my ( $ owner ) = $ factoids - > get_data ( $ channel , $ trigger , 'owner' ) =~ m/([^!]+)/ ;
2013-07-31 15:29:37 +02:00
2020-01-25 21:28:05 +01:00
if ( ( lc $ nick ne lc $ owner ) and ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ channel , "$nick!$user\@$host" ) ) ) {
2020-01-15 03:10:53 +01:00
return "You are not the owner of $trigger_name for $channel_name." ;
2010-06-20 08:16:48 +02:00
}
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ channel , $ trigger , 'locked' ) ) {
2020-01-15 03:10:53 +01:00
return "/say $trigger_name is locked; unlock before deleting." ;
2013-09-13 23:48:19 +02:00
}
2020-02-13 22:31:36 +01:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host removed [$channel][$trigger][" . $ factoids - > get_data ( $ 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 ) ;
2020-01-15 03:10:53 +01:00
return "/say $trigger_name removed from $channel_name." ;
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 ) = @ _ ;
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
2010-03-22 08:33:44 +01:00
my % hash ;
my $ factoid_count = 0 ;
2020-02-13 22:31:36 +01:00
foreach my $ channel ( $ factoids - > get_keys ) {
foreach my $ command ( $ factoids - > get_keys ( $ channel ) ) {
if ( $ factoids - > get_data ( $ 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 ) = @ _ ;
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
2019-06-09 22:57:52 +02:00
$ stuff - > { preserve_whitespace } = 1 ;
2019-07-01 05:12:41 +02:00
my $ usage = "Usage: factshow [-p] [channel] <keyword>; -p to paste" ;
return $ usage if not $ arguments ;
my $ getopt_error ;
local $ SIG { __WARN__ } = sub {
$ getopt_error = shift ;
chomp $ getopt_error ;
} ;
my ( $ paste ) ;
my ( $ ret , $ args ) = GetOptionsFromString ( $ arguments ,
'p' = > \ $ paste ) ;
return "/say $getopt_error -- $usage" if defined $ getopt_error ;
return "Too many arguments -- $usage" if @$ args > 2 ;
return "Missing argument -- $usage" if not @$ args ;
my ( $ chan , $ trig ) = @$ args ;
2020-02-13 22:31:36 +01:00
$ chan = $ from if not defined $ trig ;
2019-07-01 05:12:41 +02:00
$ args = join ( ' ' , map { $ _ = "'$_'" if $ _ =~ m/ / ; $ _ ; } @$ args ) ;
my ( $ channel , $ trigger ) = $ self - > find_factoid_with_optional_channel ( $ from , $ args , 'factshow' , usage = > $ usage ) ;
2017-08-27 09:56:55 +02:00
return $ channel if not defined $ trigger ; # if $trigger is not defined, $channel is an error message
2015-07-22 00:07:56 +02:00
2020-02-13 22:31:36 +01:00
my $ channel_name = $ factoids - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ factoids - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
2019-07-01 05:21:09 +02:00
2020-01-15 03:10:53 +01:00
my $ result = "$trigger_name: " ;
2019-07-01 05:21:09 +02:00
2019-07-01 05:12:41 +02:00
if ( $ paste ) {
2020-02-13 22:31:36 +01:00
$ result . = $ self - > { pbot } - > { webpaste } - > paste ( $ factoids - > get_data ( $ channel , $ trigger , 'action' ) , no_split = > 1 ) ;
2020-01-15 03:10:53 +01:00
$ result = "[$channel_name] $result" if $ channel ne lc $ chan ;
2019-07-01 05:21:09 +02:00
return $ result ;
2019-07-01 05:12:41 +02:00
}
2020-02-13 22:31:36 +01:00
$ result . = $ factoids - > get_data ( $ channel , $ trigger , 'action' ) ;
$ result . = ' [module]' if $ factoids - > get_data ( $ channel , $ trigger , 'type' ) eq 'module' ;
2020-01-15 03:10:53 +01:00
$ result = "[$channel_name] $result" if $ channel ne lc $ 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> ) {
2019-07-01 07:19:04 +02:00
my ( $ timestamp , $ hostmask , $ msg ) ;
( $ timestamp , $ hostmask , $ msg ) = eval {
my $ h = decode_json $ line ;
return ( $ h - > { ts } , $ h - > { hm } , $ h - > { msg } ) ;
} ;
2020-02-13 22:31:36 +01:00
( $ timestamp , $ hostmask , $ msg ) = split /\s+/ , $ line , 3 if $@ ;
$ hostmask =~ s/!.*$// if not $ show_hostmask ;
2015-12-15 01:13:43 +01:00
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 ) = @ _ ;
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
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
2020-02-13 22:31:36 +01:00
my $ channel_name = $ factoids - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ factoids - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
2020-02-13 22:31:36 +01:00
my $ created_ago = ago ( gettimeofday - $ factoids - > get_data ( $ channel , $ trigger , 'created_on' ) ) ;
my $ ref_ago = ago ( gettimeofday - $ factoids - > get_data ( $ channel , $ trigger , 'last_referenced_on' ) ) if defined $ factoids - > get_data ( $ channel , $ trigger , 'last_referenced_on' ) ;
2010-06-21 12:44:15 +02:00
2010-03-22 08:33:44 +01:00
# factoid
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ channel , $ trigger , 'type' ) eq 'text' ) {
return "/say $trigger_name: Factoid submitted by " . $ factoids - > get_data ( $ channel , $ trigger , 'owner' ) . " for $channel_name on " . localtime ( $ factoids - > get_data ( $ channel , $ trigger , 'created_on' ) ) . " [$created_ago], " . ( defined $ factoids - > get_data ( $ channel , $ trigger , 'edited_by' ) ? 'last edited by ' . $ factoids - > get_data ( $ channel , $ trigger , 'edited_by' ) . ' on ' . localtime ( $ factoids - > get_data ( $ channel , $ trigger , 'edited_on' ) ) . " [" . ago ( gettimeofday - $ factoids - > get_data ( $ channel , $ trigger , 'edited_on' ) ) . "], " : "" ) . "referenced " . $ factoids - > get_data ( $ channel , $ trigger , 'ref_count' ) . ' times (last by ' . $ factoids - > get_data ( $ channel , $ trigger , 'ref_user' ) . ( $ factoids - > exists ( $ channel , $ trigger , 'last_referenced_on' ) ? ' on ' . localtime ( $ factoids - > get_data ( $ channel , $ trigger , 'last_referenced_on' ) ) . " [$ref_ago]" : '' ) . ')' ;
2010-03-22 08:33:44 +01:00
}
# module
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ channel , $ trigger , 'type' ) eq 'module' ) {
2014-12-30 08:15:46 +01:00
my $ module_repo = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'module_repo' ) ;
2020-02-13 22:31:36 +01:00
$ module_repo . = $ factoids - > get_data ( $ channel , $ trigger , 'workdir' ) . '/' if $ factoids - > exists ( $ channel , $ trigger , 'workdir' ) ;
return "/say $trigger_name: Module loaded by " . $ factoids - > get_data ( $ channel , $ trigger , 'owner' ) . " for $channel_name on " . localtime ( $ factoids - > get_data ( $ channel , $ trigger , 'created_on' ) ) . " [$created_ago] -> $module_repo" . $ factoids - > get_data ( $ channel , $ trigger , 'action' ) . ', used ' . $ factoids - > get_data ( $ channel , $ trigger , 'ref_count' ) . ' times (last by ' . $ factoids - > get_data ( $ channel , $ trigger , 'ref_user' ) . ( $ factoids - > exists ( $ channel , $ trigger , 'last_referenced_on' ) ? ' on ' . localtime ( $ factoids - > get_data ( $ channel , $ trigger , 'last_referenced_on' ) ) . " [$ref_ago]" : '' ) . ')' ;
2010-03-22 08:33:44 +01:00
}
# regex
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ channel , $ trigger , 'type' ) eq 'regex' ) {
return "/say $trigger_name: Regex created by " . $ factoids - > get_data ( $ channel , $ trigger , 'owner' ) . " for $channel_name on " . localtime ( $ factoids - > get_data ( $ channel , $ trigger , 'created_on' ) ) . " [$created_ago], " . ( defined $ factoids - > get_data ( $ channel , $ trigger , 'edited_by' ) ? 'last edited by ' . $ factoids - > get_data ( $ channel , $ trigger , 'edited_by' ) . ' on ' . localtime ( $ factoids - > get_data ( $ channel , $ trigger , 'edited_on' ) ) . " [" . ago ( gettimeofday - $ factoids - > get_data ( $ channel , $ trigger , 'edited_on' ) ) . "], " : "" ) . ' used ' . $ factoids - > get_data ( $ channel , $ trigger , 'ref_count' ) . ' times (last by ' . $ factoids - > get_data ( $ channel , $ trigger , 'ref_user' ) . ( $ factoids - > exists ( $ channel , $ trigger , 'last_referenced_on' ) ? ' on ' . localtime ( $ factoids - > get_data ( $ channel , $ trigger , 'last_referenced_on' ) ) . " [$ref_ago]" : '' ) . ')' ;
2010-03-22 08:33:44 +01:00
}
2020-01-15 03:10:53 +01: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 ) = @ _ ;
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
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 ) {
2020-02-13 22:31:36 +01:00
foreach my $ chan ( sort $ factoids - > get_keys ) {
2011-01-27 01:49:36 +01:00
next if lc $ chan ne lc $ channel ;
2020-02-13 22:31:36 +01:00
foreach my $ command ( sort { $ factoids - > get_data ( $ chan , $ b , 'ref_count' ) <=> $ factoids - > get_data ( $ chan , $ a , 'ref_count' ) } $ factoids - > get_keys ( $ chan ) ) {
if ( $ factoids - > get_data ( $ chan , $ command , 'ref_count' ) > 0 and $ factoids - > get_data ( $ chan , $ command , 'type' ) eq 'text' ) {
$ text . = $ factoids - > get_data ( $ chan , $ command , '_name' ) . ' (' . $ factoids - > get_data ( $ 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" ) {
2020-02-13 22:31:36 +01:00
foreach my $ chan ( sort $ factoids - > get_keys ) {
2011-01-27 01:49:36 +01:00
next if lc $ chan ne lc $ channel ;
2020-02-13 22:31:36 +01:00
foreach my $ command ( sort { $ factoids - > get_data ( $ chan , $ b , 'created_on' ) <=> $ factoids - > get_data ( $ chan , $ a , 'created_on' ) } $ factoids - > get_keys ( $ chan ) ) {
my $ ago = concise ago gettimeofday - $ factoids - > get_data ( $ chan , $ command , 'created_on' ) ;
my $ owner = $ factoids - > get_data ( $ chan , $ command , 'owner' ) ;
2015-12-22 17:12:59 +01:00
$ owner =~ s/!.*$// ;
2020-02-13 22:31:36 +01:00
$ text . = ' ' . $ factoids - > get_data ( $ chan , $ command , '_name' ) . " [$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 ;
2020-02-13 22:31:36 +01:00
foreach my $ chan ( sort $ factoids - > get_keys ) {
2011-01-27 01:49:36 +01:00
next if lc $ chan ne lc $ channel ;
2020-02-13 22:31:36 +01:00
foreach my $ command ( sort { ( $ factoids - > get_data ( $ chan , $ b , 'last_referenced_on' ) || 0 ) <=> ( $ factoids - > get_data ( $ chan , $ a , 'last_referenced_on' ) || 0 ) } $ factoids - > get_keys ( $ chan ) ) {
2020-01-15 03:10:53 +01:00
next if $ command eq '_name' ;
2020-02-13 22:31:36 +01:00
my $ ref_user = lc $ factoids - > get_data ( $ chan , $ command , 'ref_user' ) ;
if ( $ ref_user =~ /\Q$args\E/i ) {
if ( $ user ne $ ref_user && not $ user =~ /$ref_user/i ) {
$ user . = " ($ref_user)" ;
2011-01-27 01:49:36 +01:00
}
2020-02-13 22:31:36 +01:00
my $ ago = $ factoids - > get_data ( $ chan , $ command , 'last_referenced_on' ) ? concise ago ( gettimeofday - $ factoids - > get_data ( $ chan , $ command , 'last_referenced_on' ) ) : "unknown" ;
$ text . = ' ' . $ factoids - > get_data ( $ chan , $ command , '_name' ) . " [$ago]\n" ;
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
$ 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 ) = @ _ ;
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
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 {
2020-02-13 22:31:36 +01:00
foreach my $ channel ( $ factoids - > get_keys ) {
foreach my $ command ( $ factoids - > get_keys ( $ channel ) ) {
2020-01-15 03:10:53 +01:00
next if $ command eq '_name' ;
2020-02-13 22:31:36 +01:00
next if $ factoids - > get_data ( $ channel , $ command , 'type' ) ne 'text' ;
2019-06-26 18:34:19 +02:00
$ total + + ;
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ 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 ) = @ _ ;
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]" ;
2020-02-13 22:31:36 +01:00
return $ usage if not defined $ arguments ;
2019-05-13 09:07:08 +02:00
2020-02-13 22:31:36 +01:00
my $ factoids = $ self - > { pbot } - > { factoids } - > { factoids } ;
2019-05-09 05:48:26 +02:00
my ( $ channel , $ owner , $ refby , $ editby , $ use_regex ) ;
$ 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 ;
2020-02-13 22:31:36 +01:00
$ argtype = "owned by $owner" if $ owner ne '.*' ;
2010-03-26 09:58:25 +01:00
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
2020-02-13 22:31:36 +01:00
foreach my $ chan ( sort $ factoids - > get_keys ) {
2018-05-12 11:52:52 +02:00
next if defined $ channel and $ chan !~ /^$channel$/i ;
2020-02-13 22:31:36 +01:00
foreach my $ trigger ( sort $ factoids - > get_keys ( $ chan ) ) {
2020-01-15 03:10:53 +01:00
next if $ trigger eq '_name' ;
2020-02-13 22:31:36 +01:00
if ( $ factoids - > get_data ( $ chan , $ trigger , 'type' ) eq 'text' or $ factoids - > get_data ( $ chan , $ trigger , 'type' ) eq 'regex' ) {
if ( $ factoids - > get_data ( $ chan , $ trigger , 'owner' ) =~ /^$owner$/i
&& $ factoids - > get_data ( $ chan , $ trigger , 'ref_user' ) =~ /^$refby$/i
&& ( $ factoids - > exists ( $ chan , $ trigger , 'edited_by' ) ? $ factoids - > get_data ( $ chan , $ trigger , 'edited_by' ) =~ /^$editby$/i : 1 ) ) {
next if ( $ arguments ne "" && $ factoids - > get_data ( $ chan , $ trigger , 'action' ) !~ /$regex/i && $ trigger !~ /$regex/i ) ;
2010-06-29 06:33:27 +02:00
$ i + + ;
2019-05-28 18:19:42 +02:00
if ( $ chan ne $ last_chan ) {
2020-02-13 22:31:36 +01:00
$ text . = $ chan eq '.*' ? '[global channel] ' : '[' . $ factoids - > get_data ( $ chan , '_name' ) . '] ' ;
2010-06-29 06:33:27 +02:00
$ last_chan = $ chan ;
}
2020-02-13 22:31:36 +01:00
my $ trigger_name = $ factoids - > get_data ( $ chan , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
$ text . = "$trigger_name " ;
$ last_trigger = $ trigger_name ;
2010-06-29 06:33:27 +02:00
}
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 ;
2020-02-13 22:31:36 +01:00
return "Found one factoid submitted for " . ( $ last_chan eq '.*' ? 'global channel' : $ factoids - > get_data ( $ last_chan , '_name' ) ) . ' ' . $ argtype . ": $last_trigger is " . $ factoids - > get_data ( $ 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 08:12:52 +02:00
my $ chans = ( defined $ channel ? ( $ channel eq '.*' ? 'global channel' : $ channel ) : 'any channels' ) ;
2020-01-15 03:10:53 +01: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 ) = @ _ ;
2020-02-13 22:31:36 +01:00
my $ factoids_data = $ self - > { pbot } - > { factoids } - > { factoids } ;
2019-07-01 07:19:04 +02:00
my ( $ channel , $ trigger , $ keyword , $ delim , $ tochange , $ changeto , $ modifier , $ url ) ;
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 ) ;
2019-07-01 07:19:04 +02:00
if ( $ arg_count >= 4 and ( $ args - > [ 0 ] =~ m/^#/ or $ args - > [ 0 ] eq '.*' or lc $ args - > [ 0 ] eq 'global' ) and ( $ args - > [ 2 ] eq '-url' ) ) {
$ channel = $ args - > [ 0 ] ;
$ keyword = $ args - > [ 1 ] ;
$ url = $ args - > [ 3 ] ;
$ needs_disambig = 0 ;
} elsif ( $ arg_count >= 3 and $ args - > [ 1 ] eq '-url' ) {
$ keyword = $ args - > [ 0 ] ;
$ url = $ args - > [ 2 ] ;
$ channel = $ from ;
$ needs_disambig = 1 ;
} elsif ( $ arg_count >= 3 and ( $ args - > [ 0 ] =~ m/^#/ or $ args - > [ 0 ] eq '.*' or lc $ args - > [ 0 ] eq 'global' ) and ( $ args - > [ 2 ] =~ m/^s([[:punct:]])/ ) ) {
2019-06-09 22:57:52 +02:00
$ delim = $ 1 ;
$ channel = $ args - > [ 0 ] ;
$ keyword = $ args - > [ 1 ] ;
2020-02-02 07:19:53 +01:00
( $ sub ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ args , 1 , 2 , 1 ) ;
2019-06-09 22:57:52 +02:00
$ 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 ;
2020-02-02 07:19:53 +01:00
( $ sub ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ args , 1 , 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
2019-07-01 07:19:04 +02:00
if ( defined $ sub ) {
$ delim = quotemeta $ delim ;
if ( $ sub =~ /^s$delim(.*?)$delim(.*)$delim(.*)$/ ) {
$ tochange = $ 1 ;
$ changeto = $ 2 ;
$ modifier = $ 3 ;
} elsif ( $ sub =~ /^s$delim(.*?)$delim(.*)$/ ) {
$ tochange = $ 1 ;
$ changeto = $ 2 ;
$ modifier = '' ;
}
2010-03-22 08:33:44 +01:00
}
}
2019-07-01 07:19:04 +02:00
if ( not defined $ channel or ( not defined $ changeto and not defined $ url ) ) {
return "Usage: factchange [channel] <keyword> (s/<pattern>/<replacement>/ | -url <paste site>)" ;
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
2020-02-13 22:31:36 +01:00
my $ channel_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , '_name' ) ;
my $ trigger_name = $ self - > { pbot } - > { factoids } - > { factoids } - > get_data ( $ channel , $ trigger , '_name' ) ;
2020-01-15 03:10:53 +01:00
$ channel_name = 'global' if $ channel_name eq '.*' ;
$ trigger_name = "\"$trigger_name\"" if $ trigger_name =~ / / ;
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 ) {
2020-01-15 03:10:53 +01:00
return "/say $trigger_name belongs to $channel_name, but this is $from_chan. Please switch to $channel_name or use /msg to change this factoid." ;
2017-08-02 06:33:57 +02:00
}
2020-02-04 02:19:04 +01:00
my $ userinfo = $ self - > { pbot } - > { users } - > loggedin ( $ channel , "$nick!$user\@$host" ) ;
2020-02-13 22:31:36 +01:00
if ( $ factoids_data - > get_data ( $ channel , $ trigger , 'locked' ) ) {
2020-02-04 02:19:04 +01:00
return "/say $trigger_name is locked and cannot be changed." if not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'admin' ) ;
2016-11-17 04:07:01 +01:00
2020-02-13 22:31:36 +01:00
if ( $ factoids_data - > exists ( $ channel , $ trigger , 'cap-override' ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'botowner' ) ) {
2020-02-04 02:19:04 +01:00
return "/say $trigger_name is locked with a cap-override set and cannot be changed until the override is removed." ;
2016-11-17 04:07:01 +01:00
}
2013-09-13 23:48:19 +02:00
}
2020-02-13 22:31:36 +01:00
my $ action = $ factoids_data - > get_data ( $ channel , $ trigger , 'action' ) ;
2019-07-01 07:19:04 +02:00
if ( defined $ url ) {
2020-01-15 03:10:53 +01:00
# FIXME: move this to registry
2019-07-01 07:19:04 +02:00
if ( $ url !~ m/^https?:\/\/(?:sprunge.us|ix.io)\/\w+$/ ) {
2019-07-02 06:36:08 +02:00
return "Invalid URL: acceptable URLs are: http://sprunge.us, http://ix.io" ;
2019-07-01 07:19:04 +02:00
}
my $ ua = LWP::UserAgent - > new ( timeout = > 10 ) ;
my $ response = $ ua - > get ( $ url ) ;
if ( $ response - > is_success ) {
$ action = $ response - > decoded_content ;
} else {
return "Failed to get URL: " . $ response - > status_line ;
}
} else {
my $ ret = eval {
use re::engine::RE2 - strict = > 1 ;
my $ changed ;
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 | ;
}
2018-02-19 22:45:24 +01:00
} else {
2019-07-01 07:19:04 +02:00
if ( $ insensitive ) {
$ changed = $ action =~ s | $ tochange | $ 1 $ changeto $ magic | i ;
} else {
$ changed = $ action =~ s | $ tochange | $ 1 $ changeto $ magic | ;
}
2018-02-19 22:45:24 +01:00
}
2019-07-01 07:19:04 +02:00
if ( $ changed ) {
$ count + + ;
if ( $ count == $ max ) {
$ action =~ s/$magic// ;
last ;
}
$ tochange = "$magic(.*?)$tochange" if $ count == 1 ;
} else {
$ changed = $ count ;
$ action =~ s/$magic// if $ changed ;
2018-02-19 22:45:24 +01:00
last ;
}
}
2019-07-01 07:19:04 +02:00
} elsif ( $ modifier eq 'i' ) {
$ changed = $ action =~ s | $ tochange | $ changeto | i ;
} else {
$ changed = $ action =~ s | $ tochange | $ changeto | ;
2018-02-19 22:45:24 +01:00
}
2017-09-29 21:07:50 +02:00
2019-07-01 07:19:04 +02:00
if ( not $ changed ) {
$ self - > { pbot } - > { logger } - > log ( "($from) $nick!$user\@$host: failed to change '$trigger' 's$delim$tochange$delim$changeto$delim\n" ) ;
return "Change $trigger failed." ;
2017-08-25 00:18:41 +02:00
}
2019-07-01 07:19:04 +02:00
return "" ;
} ;
2017-08-25 00:18:41 +02:00
2020-01-15 03:10:53 +01:00
if ( $@ ) {
my $ err = $@ ;
$ err =~ s/ at PBot\/FactoidCommand.*$// ;
return "/msg $nick Change $trigger_name failed: $err" ;
}
2019-07-01 07:19:04 +02:00
return $ ret if length $ ret ;
}
2017-08-25 00:18:41 +02:00
2020-02-04 02:19:04 +01:00
if ( length $ action > 8000 and not $ self - > { pbot } - > { capabilities } - > userhas ( $ userinfo , 'admin' ) ) {
2020-01-15 03:10:53 +01:00
return "Change $trigger_name failed; result is too long." ;
2019-07-01 07:19:04 +02:00
}
2017-08-25 00:18:41 +02:00
2019-07-01 07:19:04 +02:00
if ( not length $ action ) {
2020-01-15 03:10:53 +01:00
return "Change $trigger_name failed; factoids cannot be empty." ;
2019-07-01 07:19:04 +02:00
}
$ self - > { pbot } - > { logger } - > log ( "($from) $nick!$user\@$host: changed '$trigger' 's/$tochange/$changeto/\n" ) ;
2020-02-13 22:31:36 +01:00
$ factoids_data - > set ( $ channel , $ trigger , 'action' , $ action , 1 ) ;
$ factoids_data - > set ( $ channel , $ trigger , 'edited_by' , "$nick!$user\@$host" , 1 ) ;
$ factoids_data - > set ( $ channel , $ trigger , 'edited_on' , gettimeofday ) ;
$ self - > log_factoid ( $ channel , $ trigger , "$nick!$user\@$host" , "changed to $action" ) ;
return "Changed: $trigger_name is $action" ;
2010-03-22 08:33:44 +01:00
}
1 ;