2021-06-21 00:10:16 +02:00
# File: ActionTrigger.pm
#
# Purpose: provides interface to set/remove/modify regular expression triggers
2021-06-24 01:06:15 +02:00
# which invoke bot commands when matched against IRC messages.
2020-02-02 07:20:44 +01:00
#
2021-06-21 00:10:16 +02:00
# Usage: actiontrigger add <#channel or global> <capability> <rate-limit (in seconds)> <regex trigger> <command>
2020-02-20 17:08:38 +01:00
#
2021-06-24 01:06:15 +02:00
# Note that ActionTrigger does not match on raw IRC lines. It matches on a
# simplified message format:
#
# "<hostmask> <action> <arguments>"
#
# where <action> can be PRIVMSG, ACTION, KICK, JOIN, PART or QUIT.
#
2020-02-02 07:20:44 +01:00
# Examples:
#
2020-02-02 07:29:56 +01:00
# Greet a nick when they join the channel:
2020-02-20 05:59:54 +01:00
# actiontrigger add #channel none 0 ^(?i)([^!]+)![^\s]+.JOIN echo Hi $1, welcome to $channel!
2020-02-02 07:29:56 +01:00
#
2020-02-20 05:59:54 +01:00
# Same, but via private message (set capability to "admin" to use `msg` admin command):
2021-06-12 21:38:57 +02:00
# actiontrigger add #channel admin 0 ^(?i)([^!]+)![^\s]+.JOIN msg $1 Hi $1, welcome to $channel!
2020-02-02 08:03:44 +01:00
#
2020-02-20 05:59:54 +01:00
# Kick a nick if they say a naughty thing. Set capability to "can-kick" to use `kick` admin command.
# actiontrigger add global can-kick 0 "^(?i)([^!]+)![^\s]+.PRIVMSG.*bad phrase" kick $1 Do you talk to your mother with that mouth?
2020-02-02 07:29:56 +01:00
#
# Say something when a keyword is seen, but only once every 5 minutes:
2020-02-20 05:59:54 +01:00
# actiontrigger add global none 300 "some phrase" echo Something!
2020-02-02 07:20:44 +01:00
#
2020-02-02 08:03:44 +01:00
# Capture a part of somebody's message.
2020-02-20 05:59:54 +01:00
# actiontrigger add #channel none 0 "(?i)how is the weather (?:in|for) (.*) today" weather $1
2020-02-02 08:03:44 +01:00
#
2020-02-02 07:20:44 +01:00
# These are basic examples; more complex examples can be crafted.
2021-06-21 00:10:16 +02:00
2023-02-21 06:31:52 +01:00
# SPDX-FileCopyrightText: 2017-2023 Pragmatic Software <pragma78@gmail.com>
2021-07-11 00:00:22 +02:00
# SPDX-License-Identifier: MIT
2021-06-21 00:10:16 +02:00
2021-07-14 04:45:56 +02:00
package PBot::Plugin::ActionTrigger ;
use parent 'PBot::Plugin::Base' ;
2020-02-02 07:20:44 +01:00
2021-06-19 06:23:34 +02:00
use PBot::Imports ;
2017-08-13 08:43:36 +02:00
use DBI ;
use Time::Duration qw/duration/ ;
use Time::HiRes qw/gettimeofday/ ;
sub initialize {
2020-02-15 23:38:32 +01:00
my ( $ self , % conf ) = @ _ ;
2021-06-21 00:10:16 +02:00
# register bot command
2021-07-31 04:01:24 +02:00
$ self - > { pbot } - > { commands } - > add (
name = > 'actiontrigger' ,
help = > 'Manages regular expression triggers to invoke bot commands' ,
requires_cap = > 1 ,
subref = > sub { $ self - > cmd_actiontrigger ( @ _ ) } ,
) ;
2021-06-21 00:10:16 +02:00
# add capability to admin group
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-actiontrigger' , 1 ) ;
2017-08-13 08:43:36 +02:00
2021-06-21 00:10:16 +02:00
# register IRC handlers
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.public' , sub { $ self - > on_public ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.caction' , sub { $ self - > on_action ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.join' , sub { $ self - > on_join ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.part' , sub { $ self - > on_departure ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.quit' , sub { $ self - > on_departure ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.kick' , sub { $ self - > on_kick ( @ _ ) } ) ;
2017-08-13 08:43:36 +02:00
2021-06-21 00:10:16 +02:00
# database file
2020-02-15 23:38:32 +01:00
$ self - > { filename } = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/triggers.sqlite3' ;
2017-08-13 08:43:36 +02:00
2021-06-21 00:10:16 +02:00
# open and initialize database
2020-02-15 23:38:32 +01:00
$ self - > dbi_begin ;
$ self - > create_database ;
2017-08-13 08:43:36 +02:00
}
sub unload {
2021-06-21 00:10:16 +02:00
my ( $ self ) = @ _ ;
# close database
2020-02-15 23:38:32 +01:00
$ self - > dbi_end ;
2021-06-21 00:10:16 +02:00
# unregister bot command
2021-07-31 04:01:24 +02:00
$ self - > { pbot } - > { commands } - > remove ( 'actiontrigger' ) ;
2021-06-21 00:10:16 +02:00
# remove capability
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { capabilities } - > remove ( 'can-actiontrigger' ) ;
2021-06-21 00:10:16 +02:00
# remove IRC handlers
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.public' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.caction' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.join' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.part' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.quit' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.kick' ) ;
2017-08-13 08:43:36 +02:00
}
2021-06-21 00:10:16 +02:00
sub cmd_actiontrigger {
my ( $ self , $ context ) = @ _ ;
# database not available
return "Internal error." if not $ self - > { dbh } ;
my $ command = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ context - > { arglist } ) ;
given ( $ command ) {
when ( 'list' ) {
my $ channel = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ context - > { arglist } ) ;
if ( not defined $ channel ) {
if ( $ context - > { from } !~ /^#/ ) {
# used from /msg
$ channel = 'global' ;
} else {
# used in channel
$ channel = $ context - > { from } ;
}
}
elsif ( $ channel !~ m/^#/ and $ channel ne 'global' ) {
return "Invalid channel $channel. Usage: actiontrigger list [#channel or global]" ;
}
my @ triggers = $ self - > list_triggers ( $ channel ) ;
if ( not @ triggers ) {
return "No action triggers set for $channel." ;
}
else {
my $ result = "Triggers for $channel:\n" ;
my @ items ;
foreach my $ trigger ( @ triggers ) {
$ trigger - > { cap_override } // = 'none' ;
$ trigger - > { ratelimit } // = 0 ;
my $ item = "$trigger->{trigger} -> $trigger->{action}" ;
if ( $ trigger - > { cap_override } and $ trigger - > { cap_override } ne 'none' ) {
$ item . = " (capability=$trigger->{cap_override})" ;
}
if ( $ trigger - > { ratelimit } != 0 ) {
$ item . = " (ratelimit=$trigger->{ratelimit})" ;
}
push @ items , $ item ;
}
$ result . = join ",\n" , @ items ;
return $ result ;
}
}
when ( 'add' ) {
# TODO: use GetOpt flags instead of positional arguments
my $ channel ;
if ( $ context - > { from } =~ m/^#/ ) {
$ channel = $ context - > { from } ;
}
else {
$ channel = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ context - > { arglist } ) ;
if ( not defined $ channel ) {
return
"To use this command from private message the <channel> argument is required. Usage: actiontrigger add <#channel or global> <capability> <rate-limit (in seconds)> <regex trigger> <command>" ;
}
elsif ( $ channel !~ m/^#/ and $ channel ne 'global' ) {
return "Invalid channel $channel. Usage: actiontrigger add <#channel or global> <capability> <rate-limit (in seconds)> <regex trigger> <command>" ;
}
}
# split into 4 arguments, offset 0, preserving quotes
my ( $ cap_override , $ ratelimit , $ trigger , $ action ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ context - > { arglist } , 4 , 0 , 1 ) ;
if ( not defined $ trigger or not defined $ action ) {
if ( $ context - > { from } !~ m/^#/ ) {
return
"To use this command from private message the <channel> argument is required. Usage: actiontrigger add <#channel or global> <capability> <rate-limit (in seconds)> <regex trigger> <command>" ;
} else {
return "Usage: actiontrigger add <capability> <rate-limit (in seconds)> <regex trigger> <command>" ;
}
}
if ( defined $ self - > get_trigger ( $ channel , $ trigger ) ) {
return "Trigger already exists." ;
}
if ( $ ratelimit !~ m/^\d+$/ ) {
return "$context->{nick}: Missing rate-limit argument?\n" ;
}
if ( $ cap_override ne 'none' ) {
if ( not $ self - > { pbot } - > { capabilities } - > exists ( $ cap_override ) ) {
return "$context->{nick}: Capability '$cap_override' does not exist. Use 'none' to omit.\n" ;
}
my $ u = $ self - > { pbot } - > { users } - > find_user ( $ channel , $ context - > { hostmask } ) ;
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , $ cap_override ) ) {
return "You may not set a capability that you do not have." ;
}
}
if ( $ self - > add_trigger ( $ channel , $ trigger , $ action , $ context - > { hostmask } , $ cap_override , $ ratelimit ) ) {
return "Trigger added." ;
} else {
return "Failed to add trigger." ;
}
}
when ( 'delete' ) {
my $ channel ;
if ( $ context - > { from } =~ m/^#/ ) {
$ channel = $ context - > { from } ;
}
else {
$ channel = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ context - > { arglist } ) ;
if ( $ channel !~ m/^#/ and $ channel ne 'global' ) {
return "To use this command from private message the <channel> argument is required. Usage: actiontrigger delete <#channel or global> <regex trigger>" ;
}
}
my ( $ trigger ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ context - > { arglist } , 1 ) ;
if ( not defined $ trigger ) {
if ( $ context - > { from } !~ m/^#/ ) {
return "To use this command from private message the <channel> argument is required. Usage: from private message: actiontrigger delete <channel> <regex trigger>" ;
} else {
return "Usage: actiontrigger delete <regex trigger>" ;
}
}
if ( not defined $ self - > get_trigger ( $ channel , $ trigger ) ) {
return "No such trigger." ;
} else {
$ self - > delete_trigger ( $ channel , $ trigger ) ;
return "Trigger deleted." ;
}
}
default {
if ( $ context - > { from } !~ m/^#/ ) {
return
"Usage from private message: actiontrigger list [#channel or global] | actiontrigger add <#channel or global> <capability> <rate-limit (in seconds)> <regex trigger> <command> | actiontrigger delete <#channel or global> <regex trigger>" ;
} else {
return
"Usage: actiontrigger list [#channel or global] | actiontrigger add <capability> <rate-limit (in seconds)> <regex trigger> <command> | actiontrigger delete <regex>" ;
}
}
}
}
2017-08-13 08:43:36 +02:00
sub create_database {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
return if not $ self - > { dbh } ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
eval {
$ self - > { dbh } - > do ( << SQL ) ;
2017-08-13 08:43:36 +02:00
CREATE TABLE IF NOT EXISTS Triggers (
2020-02-20 05:59:54 +01:00
channel TEXT ,
trigger TEXT ,
action TEXT ,
owner TEXT ,
cap_override TEXT ,
2021-06-21 00:10:16 +02:00
ratelimit INTEGER ,
2020-02-20 05:59:54 +01:00
lastused NUMERIC
2017-08-13 08:43:36 +02:00
)
SQL
2020-02-15 23:38:32 +01:00
} ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "ActionTrigger create database failed: $@" ) if $@ ;
2017-08-13 08:43:36 +02:00
}
sub dbi_begin {
2020-02-15 23:38:32 +01:00
my ( $ self ) = @ _ ;
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
eval {
$ self - > { dbh } = DBI - > connect ( "dbi:SQLite:dbname=$self->{filename}" , "" , "" , { RaiseError = > 1 , PrintError = > 0 , AutoInactiveDestroy = > 1 , sqlite_unicode = > 1 } )
or die $ DBI:: errstr ;
} ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( $@ ) {
$ self - > { pbot } - > { logger } - > log ( "Error opening ActionTrigger database: $@" ) ;
return 0 ;
} else {
return 1 ;
}
2017-08-13 08:43:36 +02:00
}
sub dbi_end {
2020-02-15 23:38:32 +01:00
my ( $ self ) = @ _ ;
return if not $ self - > { dbh } ;
$ self - > { dbh } - > disconnect ;
delete $ self - > { dbh } ;
2017-08-13 08:43:36 +02:00
}
sub add_trigger {
2021-06-21 00:10:16 +02:00
my ( $ self , $ channel , $ trigger , $ action , $ owner , $ cap_override , $ ratelimit ) = @ _ ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
return 0 if $ self - > get_trigger ( $ channel , $ trigger ) ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
eval {
2021-06-21 00:10:16 +02:00
my $ sth = $ self - > { dbh } - > prepare ( 'INSERT INTO Triggers (channel, trigger, action, owner, cap_override, ratelimit, lastused) VALUES (?, ?, ?, ?, ?, ?, 0)' ) ;
$ sth - > execute ( lc $ channel , $ trigger , $ action , $ owner , $ cap_override , $ ratelimit ) ;
2020-02-15 23:38:32 +01:00
} ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( $@ ) {
$ self - > { pbot } - > { logger } - > log ( "Add trigger failed: $@" ) ;
return 0 ;
}
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
return 1 ;
2017-08-13 08:43:36 +02:00
}
sub delete_trigger {
2020-02-15 23:38:32 +01:00
my ( $ self , $ channel , $ trigger ) = @ _ ;
return 0 if not $ self - > get_trigger ( $ channel , $ trigger ) ;
my $ sth = $ self - > { dbh } - > prepare ( 'DELETE FROM Triggers WHERE channel = ? AND trigger = ?' ) ;
$ sth - > execute ( lc $ channel , $ trigger ) ;
return 1 ;
2017-08-13 08:43:36 +02:00
}
sub list_triggers {
2020-02-15 23:38:32 +01:00
my ( $ self , $ channel ) = @ _ ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
my $ triggers = eval {
my $ sth ;
2018-02-04 01:06:42 +01:00
2020-02-15 23:38:32 +01:00
if ( $ channel eq '*' ) {
$ channel = 'global' ;
2021-06-21 00:10:16 +02:00
$ sth = $ self - > { dbh } - > prepare ( 'SELECT * FROM Triggers WHERE channel != ?' ) ;
2020-02-15 23:38:32 +01:00
} else {
$ sth = $ self - > { dbh } - > prepare ( 'SELECT * FROM Triggers WHERE channel = ?' ) ;
}
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
$ sth - > execute ( lc $ channel ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( $@ ) { $ self - > { pbot } - > { logger } - > log ( "List triggers failed: $@" ) ; }
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
$ triggers = [] if not defined $ triggers ;
return @$ triggers ;
2017-08-13 08:43:36 +02:00
}
2019-11-15 02:24:51 +01:00
sub update_trigger {
2020-02-15 23:38:32 +01:00
my ( $ self , $ channel , $ trigger , $ data ) = @ _ ;
2019-11-15 02:24:51 +01:00
2020-02-15 23:38:32 +01:00
eval {
my $ sql = 'UPDATE Triggers SET ' ;
2019-11-15 02:24:51 +01:00
2021-06-21 00:10:16 +02:00
my @ triggers ;
2020-02-15 23:38:32 +01:00
foreach my $ key ( keys %$ data ) {
2021-06-21 00:10:16 +02:00
push @ triggers , "$key = ?" ;
2020-02-15 23:38:32 +01:00
}
2019-11-15 02:24:51 +01:00
2021-06-21 00:10:16 +02:00
$ sql . = join ', ' , @ triggers ;
2020-02-15 23:38:32 +01:00
$ sql . = "WHERE trigger = ? AND channel = ?" ;
2021-06-21 00:10:16 +02:00
my $ sth = $ self - > { dbh } - > prepare ( $ sql ) ;
2020-02-15 23:38:32 +01:00
my $ param = 1 ;
foreach my $ key ( keys %$ data ) { $ sth - > bind_param ( $ param + + , $ data - > { $ key } ) ; }
2019-11-15 02:24:51 +01:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param + + , $ trigger ) ;
$ sth - > bind_param ( $ param , $ channel ) ;
2021-06-21 00:10:16 +02:00
$ sth - > execute ;
2020-02-15 23:38:32 +01:00
} ;
2019-11-15 02:24:51 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Update trigger $channel/$trigger failed: $@\n" ) if $@ ;
2019-11-15 02:24:51 +01:00
}
2017-08-13 08:43:36 +02:00
sub get_trigger {
2020-02-15 23:38:32 +01:00
my ( $ self , $ channel , $ trigger ) = @ _ ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
my $ row = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT * FROM Triggers WHERE channel = ? AND trigger = ?' ) ;
$ sth - > execute ( lc $ channel , $ trigger ) ;
2021-06-21 00:10:16 +02:00
my $ row = $ sth - > fetchrow_hashref ;
2020-02-15 23:38:32 +01:00
return $ row ;
} ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( $@ ) {
$ self - > { pbot } - > { logger } - > log ( "Get trigger failed: $@" ) ;
return undef ;
}
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
return $ row ;
2017-08-13 08:43:36 +02:00
}
2020-02-02 07:20:44 +01:00
sub on_kick {
2020-02-15 23:38:32 +01:00
my ( $ self , $ event_type , $ event ) = @ _ ;
2021-06-21 00:10:16 +02:00
# don't handle this event if it was caused by a bot command
2020-02-15 23:38:32 +01:00
return 0 if $ event - > { interpreted } ;
2021-06-21 00:10:16 +02:00
my ( $ nick , $ user , $ host ) = (
2023-01-31 14:44:34 +01:00
$ event - > nick ,
$ event - > user ,
$ event - > host
2021-06-21 00:10:16 +02:00
) ;
my ( $ victim , $ reason ) = (
2023-01-31 14:44:34 +01:00
$ event - > to ,
$ event - > { args } [ 1 ]
2021-06-21 00:10:16 +02:00
) ;
2023-01-31 14:44:34 +01:00
my $ channel = $ event - > { args } [ 0 ] ;
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
$ self - > check_trigger ( $ nick , $ user , $ host , $ channel , "KICK $victim $reason" ) ;
return 0 ;
2020-02-02 07:20:44 +01:00
}
2017-08-13 08:43:36 +02:00
2020-02-02 07:20:44 +01:00
sub on_action {
2020-02-15 23:38:32 +01:00
my ( $ self , $ event_type , $ event ) = @ _ ;
2021-06-21 00:10:16 +02:00
my ( $ nick , $ user , $ host , $ msg ) = (
2023-01-31 14:44:34 +01:00
$ event - > nick ,
$ event - > user ,
$ event - > host ,
$ event - > args
2021-06-21 00:10:16 +02:00
) ;
2023-01-31 14:44:34 +01:00
my $ channel = $ event - > { to } [ 0 ] ;
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
$ msg =~ s/^\/me\s+// ;
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
$ self - > check_trigger ( $ nick , $ user , $ host , $ channel , "ACTION $msg" ) ;
return 0 ;
2020-02-02 07:20:44 +01:00
}
2017-08-13 08:43:36 +02:00
2020-02-02 07:20:44 +01:00
sub on_public {
2020-02-15 23:38:32 +01:00
my ( $ self , $ event_type , $ event ) = @ _ ;
2021-06-21 00:10:16 +02:00
my ( $ nick , $ user , $ host , $ msg ) = (
2023-01-31 14:44:34 +01:00
$ event - > nick ,
$ event - > user ,
$ event - > host ,
$ event - > args ) ;
2021-06-21 00:10:16 +02:00
2023-01-31 14:44:34 +01:00
my $ channel = $ event - > { to } [ 0 ] ;
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
$ self - > check_trigger ( $ nick , $ user , $ host , $ channel , "PRIVMSG $msg" ) ;
return 0 ;
2020-02-02 07:20:44 +01:00
}
2017-08-13 08:43:36 +02:00
2020-02-02 07:20:44 +01:00
sub on_join {
2020-02-15 23:38:32 +01:00
my ( $ self , $ event_type , $ event ) = @ _ ;
2021-06-21 00:10:16 +02:00
my ( $ nick , $ user , $ host , $ channel , $ args ) = (
2023-01-31 14:44:34 +01:00
$ event - > nick ,
$ event - > user ,
$ event - > host ,
$ event - > to ,
$ event - > args
2021-06-21 00:10:16 +02:00
) ;
2020-02-15 23:38:32 +01:00
$ self - > check_trigger ( $ nick , $ user , $ host , $ channel , "JOIN" ) ;
return 0 ;
2020-02-02 07:20:44 +01:00
}
2017-08-13 08:43:36 +02:00
2020-02-02 07:20:44 +01:00
sub on_departure {
2020-02-15 23:38:32 +01:00
my ( $ self , $ event_type , $ event ) = @ _ ;
2021-06-21 00:10:16 +02:00
my ( $ nick , $ user , $ host , $ channel , $ args ) = (
2023-01-31 14:44:34 +01:00
$ event - > nick ,
$ event - > user ,
$ event - > host ,
$ event - > to ,
$ event - > args
2021-06-21 00:10:16 +02:00
) ;
2023-01-31 14:44:34 +01:00
$ self - > check_trigger ( $ nick , $ user , $ host , $ channel , ( uc $ event - > type ) . " $args" ) ;
2020-02-15 23:38:32 +01:00
return 0 ;
2020-02-02 07:20:44 +01:00
}
sub check_trigger {
2020-02-15 23:38:32 +01:00
my ( $ self , $ nick , $ user , $ host , $ channel , $ text ) = @ _ ;
2021-06-21 00:10:16 +02:00
# database not available
2020-02-15 23:38:32 +01:00
return 0 if not $ self - > { dbh } ;
2021-06-21 00:10:16 +02:00
$ channel = lc $ channel ;
# TODO: cache these instead of loading them again every message
2020-02-15 23:38:32 +01:00
my @ triggers = $ self - > list_triggers ( $ channel ) ;
my @ globals = $ self - > list_triggers ( 'global' ) ;
push @ triggers , @ globals ;
$ text = "$nick!$user\@$host $text" ;
my $ now = gettimeofday ;
foreach my $ trigger ( @ triggers ) {
eval {
2021-06-21 00:10:16 +02:00
$ trigger - > { lastused } // = 0 ;
$ trigger - > { ratelimit } // = 0 ;
if ( $ now - $ trigger - > { lastused } >= $ trigger - > { ratelimit } and $ text =~ m/$trigger->{trigger}/ ) {
my @ stuff = ( $ 1 , $ 2 , $ 3 , $ 4 , $ 5 , $ 6 , $ 7 , $ 8 , $ 9 ) ;
2020-02-15 23:38:32 +01:00
$ trigger - > { lastused } = $ now ;
2021-06-21 00:10:16 +02:00
$ self - > update_trigger ( $ trigger - > { channel } , $ trigger - > { trigger } , { lastused = > $ now } ) ;
2020-02-15 23:38:32 +01:00
my $ action = $ trigger - > { action } ;
my $ i ;
map { + + $ i ; $ action =~ s/\$$i/$_/g ; } @ stuff ;
my ( $ n , $ u , $ h ) = $ trigger - > { owner } =~ /^([^!]+)!([^@]+)\@(.*)$/ ;
2021-06-21 00:10:16 +02:00
2020-02-15 23:38:32 +01:00
my $ command = {
2021-06-21 00:10:16 +02:00
nick = > $ n ,
user = > $ u ,
host = > $ h ,
hostmask = > "$n!$u\@$host" ,
command = > $ action ,
2020-02-15 23:38:32 +01:00
} ;
2020-02-20 05:59:54 +01:00
2021-06-21 00:10:16 +02:00
if ( $ trigger - > { cap_override } and $ trigger - > { cap_override } ne 'none' ) {
2020-02-20 05:59:54 +01:00
$ command - > { 'cap-override' } = $ trigger - > { cap_override } ;
}
2021-06-21 00:10:16 +02:00
2020-02-20 05:59:54 +01:00
my $ cap = '' ;
$ cap = " (capability=$command->{'cap-override'})" if exists $ command - > { 'cap-override' } ;
$ self - > { pbot } - > { logger } - > log ( "ActionTrigger: ($channel) $trigger->{trigger} -> $action$cap\n" ) ;
2021-06-21 00:10:16 +02:00
$ self - > { pbot } - > { interpreter } - > add_to_command_queue ( $ channel , $ command ) ;
2020-02-15 23:38:32 +01:00
}
2020-02-02 07:20:44 +01:00
} ;
2020-02-15 23:38:32 +01:00
if ( $@ ) { $ self - > { pbot } - > { logger } - > log ( "Skipping bad trigger $trigger->{trigger}: $@" ) ; }
2020-02-02 07:20:44 +01:00
}
2020-02-15 23:38:32 +01:00
2021-06-21 00:10:16 +02:00
return 0 ;
2017-08-13 08:43:36 +02:00
}
1 ;