2017-08-13 08:43:36 +02: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/.
2019-09-01 20:01:18 +02:00
package Plugins::ActionTrigger ;
2020-02-09 04:48:05 +01:00
use parent 'Plugins::Plugin' ;
2017-08-13 08:43:36 +02:00
2020-02-02 07:20:44 +01:00
# purpose: provides interface to set/remove/modify regular expression triggers
# to execute a command.
#
# Examples:
#
2020-02-02 07:29:56 +01:00
# Greet a nick when they join the channel:
2020-02-02 07:20:44 +01:00
# actiontrigger add #channel 0 0 ^(?i)([^!]+)![^\s]+.JOIN echo Hi $1, welcome to $channel!
2020-02-02 07:29:56 +01:00
#
2020-02-02 08:03:44 +01:00
# Same, but via private message (set level to 10 to use `msg` admin command):
# actiontrigger add #channel 10 0 ^(?i)([^!]+)![^\s]+.JOIN msg Hi $1, welcome to $channel!
#
# Kick a nick if they say a naughty thing. Set level to 10 to use `kick` admin command.
2020-02-02 07:39:02 +01:00
# actiontrigger add global 10 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:
# actiontrigger add global 0 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-02 08:06:48 +01:00
# actiontrigger add #channel 0 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.
2020-02-09 04:48:05 +01:00
use warnings ; use strict ;
2019-07-11 03:40:53 +02:00
use feature 'unicode_strings' ;
2017-08-13 08:43:36 +02:00
use feature 'switch' ;
no if $] >= 5.018 , warnings = > "experimental::smartmatch" ;
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 ) = @ _ ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > actiontrigger ( @ _ ) } , 'actiontrigger' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-actiontrigger' , 1 ) ;
2017-08-13 08:43:36 +02:00
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
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
2020-02-15 23:38:32 +01:00
$ self - > dbi_begin ;
$ self - > create_database ;
2017-08-13 08:43:36 +02:00
}
sub unload {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
$ self - > dbi_end ;
$ self - > { pbot } - > { commands } - > unregister ( 'actiontrigger' ) ;
$ self - > { pbot } - > { capabilities } - > remove ( 'can-actiontrigger' ) ;
$ 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
}
sub create_database {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
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 (
channel TEXT ,
trigger TEXT ,
action TEXT ,
2017-12-11 21:47:21 +01:00
owner TEXT ,
2019-11-15 02:24:51 +01:00
level INTEGER ,
repeatdelay INTEGER ,
2019-12-20 07:57: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 ) = @ _ ;
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 {
2020-02-15 23:38:32 +01:00
my ( $ self , $ channel , $ trigger , $ action , $ owner , $ level , $ repeatdelay ) = @ _ ;
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 {
my $ sth = $ self - > { dbh } - > prepare ( 'INSERT INTO Triggers (channel, trigger, action, owner, level, repeatdelay, lastused) VALUES (?, ?, ?, ?, ?, ?, 0)' ) ;
$ sth - > execute ( lc $ channel , $ trigger , $ action , $ owner , $ level , $ repeatdelay ) ;
} ;
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 ;
}
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 '*' ) {
$ sth = $ self - > { dbh } - > prepare ( 'SELECT * FROM Triggers WHERE channel != ?' ) ;
$ channel = 'global' ;
} else {
$ sth = $ self - > { dbh } - > prepare ( 'SELECT * FROM Triggers WHERE channel = ?' ) ;
}
$ 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
2020-02-15 23:38:32 +01:00
my $ comma = '' ;
foreach my $ key ( keys %$ data ) {
$ sql . = "$comma$key = ?" ;
$ comma = ", " ;
}
2019-11-15 02:24:51 +01:00
2020-02-15 23:38:32 +01:00
$ sql . = "WHERE trigger = ? AND channel = ?" ;
my $ sth = $ self - > { dbh } - > prepare ( $ sql ) ;
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 ) ;
$ sth - > execute ( ) ;
} ;
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 ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
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 ) = @ _ ;
my ( $ nick , $ user , $ host ) = ( $ event - > { event } - > nick , $ event - > { event } - > user , $ event - > { event } - > host ) ;
my ( $ victim , $ reason ) = ( $ event - > { event } - > to , $ event - > { event } - > { args } [ 1 ] ) ;
my $ channel = $ event - > { event } - > { args } [ 0 ] ;
return 0 if $ event - > { interpreted } ;
$ 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 ) = @ _ ;
my ( $ nick , $ user , $ host , $ msg ) = ( $ event - > { event } - > nick , $ event - > { event } - > user , $ event - > { event } - > host , $ event - > { event } - > args ) ;
my $ channel = $ event - > { event } - > { to } [ 0 ] ;
return 0 if $ event - > { interpreted } ;
$ msg =~ s/^\/me\s+// ;
$ 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 ) = @ _ ;
my ( $ nick , $ user , $ host , $ msg ) = ( $ event - > { event } - > nick , $ event - > { event } - > user , $ event - > { event } - > host , $ event - > { event } - > args ) ;
my $ channel = $ event - > { event } - > { to } [ 0 ] ;
return 0 if $ event - > { interpreted } ;
$ 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 ) = @ _ ;
my ( $ nick , $ user , $ host , $ channel , $ args ) = ( $ event - > { event } - > nick , $ event - > { event } - > user , $ event - > { event } - > host , $ event - > { event } - > to , $ event - > { event } - > args ) ;
$ channel = lc $ channel ;
$ 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 ) = @ _ ;
my ( $ nick , $ user , $ host , $ channel , $ args ) = ( $ event - > { event } - > nick , $ event - > { event } - > user , $ event - > { event } - > host , $ event - > { event } - > to , $ event - > { event } - > args ) ;
$ channel = lc $ channel ;
$ self - > check_trigger ( $ nick , $ user , $ host , $ channel , ( uc $ event - > { event } - > type ) . " $args" ) ;
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 ) = @ _ ;
return 0 if not $ self - > { dbh } ;
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 {
$ trigger - > { lastused } = 0 if not defined $ trigger - > { lastused } ;
$ trigger - > { repeatdelay } = 0 if not defined $ trigger - > { repeatdelay } ;
if ( $ now - $ trigger - > { lastused } >= $ trigger - > { repeatdelay } and $ text =~ m/$trigger->{trigger}/ ) {
$ trigger - > { lastused } = $ now ;
my $ data = { lastused = > $ now } ;
$ self - > update_trigger ( $ trigger - > { channel } , $ trigger - > { trigger } , $ data ) ;
my $ action = $ trigger - > { action } ;
my @ stuff = ( $ 1 , $ 2 , $ 3 , $ 4 , $ 5 , $ 6 , $ 7 , $ 8 , $ 9 ) ;
my $ i ;
map { + + $ i ; $ action =~ s/\$$i/$_/g ; } @ stuff ;
my $ delay = 0 ;
my ( $ n , $ u , $ h ) = $ trigger - > { owner } =~ /^([^!]+)!([^@]+)\@(.*)$/ ;
my $ command = {
nick = > $ n ,
user = > $ u ,
host = > $ h ,
command = > $ action ,
level = > $ trigger - > { level } // 0
} ;
$ self - > { pbot } - > { logger } - > log ( "ActionTrigger: ($channel) $trigger->{trigger} -> $action [$command->{level}]\n" ) ;
$ self - > { pbot } - > { interpreter } - > add_to_command_queue ( $ channel , $ command , $ delay ) ;
}
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
return 0 ;
2020-02-02 07:20:44 +01:00
}
sub actiontrigger {
2020-02-15 23:38:32 +01:00
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
return "Internal error." if not $ self - > { dbh } ;
my $ command = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ stuff - > { arglist } ) ;
my $ result ;
given ( $ command ) {
when ( 'list' ) {
my $ channel = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ stuff - > { arglist } ) ;
if ( not defined $ channel ) {
if ( $ from !~ /^#/ ) { $ channel = 'global' ; }
else { $ channel = $ 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 ) { $ result = "No action triggers set for $channel." ; }
else {
$ result = "Triggers for $channel:\n" ;
my $ comma = '' ;
foreach my $ trigger ( @ triggers ) {
$ trigger - > { level } // = 0 ;
$ trigger - > { repeatdelay } // = 0 ;
$ result . = "$comma$trigger->{trigger} -> $trigger->{action}" ;
$ result . = " (level=$trigger->{level})" if $ trigger - > { level } != 0 ;
$ result . = " (repeatdelay=$trigger->{repeatdelay})" if $ trigger - > { repeatdelay } != 0 ;
$ comma = ",\n" ;
}
}
2020-02-02 07:20:44 +01:00
}
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
# TODO: use GetOpt flags instead of positional arguments
when ( 'add' ) {
my $ channel ;
if ( $ from =~ m/^#/ ) { $ channel = $ from ; }
else {
$ channel = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ stuff - > { arglist } ) ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( not defined $ channel ) {
return
"To use this command from private message the <channel> argument is required. Usage: actiontrigger add <#channel or global> <level> <repeat delay (in seconds)> <regex trigger> <command>" ;
} elsif ( $ channel !~ m/^#/ and $ channel ne 'global' ) {
return "Invalid channel $channel. Usage: actiontrigger add <#channel or global> <level> <repeat delay (in seconds)> <regex trigger> <command>" ;
}
}
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
my ( $ level , $ repeatdelay , $ trigger , $ action ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 4 , 0 , 1 ) ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( not defined $ trigger or not defined $ action ) {
if ( $ from !~ m/^#/ ) {
$ result =
"To use this command from private message the <channel> argument is required. Usage: actiontrigger add <#channel or global> <level> <repeat delay (in seconds)> <regex trigger> <command>" ;
} else {
$ result = "Usage: actiontrigger add <level> <repeat delay (in seconds)> <regex trigger> <command>" ;
}
return $ result ;
}
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
my $ exists = $ self - > get_trigger ( $ channel , $ trigger ) ;
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( defined $ exists ) { return "Trigger already exists." ; }
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( $ level !~ m/^\d+$/ ) { return "$nick: Missing level argument?\n" ; }
2017-08-13 08:43:36 +02:00
2020-02-15 23:38:32 +01:00
if ( $ repeatdelay !~ m/^\d+$/ ) { return "$nick: Missing repeat delay argument?\n" ; }
2018-01-06 03:02:14 +01:00
2020-02-15 23:38:32 +01:00
if ( $ level > 0 ) {
my $ admin = $ self - > { pbot } - > { users } - > find_admin ( $ channel , "$nick!$user\@$host" ) ;
if ( not defined $ admin or $ level > $ admin - > { level } ) { return "You may not set a level higher than your own." ; }
}
2019-11-15 02:24:51 +01:00
2020-02-15 23:38:32 +01:00
if ( $ self - > add_trigger ( $ channel , $ trigger , $ action , "$nick!$user\@$host" , $ level , $ repeatdelay ) ) { $ result = "Trigger added." ; }
else { $ result = "Failed to add trigger." ; }
2017-12-11 21:47:21 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'delete' ) {
my $ channel ;
if ( $ from =~ m/^#/ ) { $ channel = $ from ; }
else {
$ channel = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ stuff - > { 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 ( $ stuff - > { arglist } , 1 ) ;
if ( not defined $ trigger ) {
if ( $ from !~ m/^#/ ) {
$ result = "To use this command from private message the <channel> argument is required. Usage: from private message: actiontrigger delete <channel> <regex trigger>" ;
} else {
$ result = "Usage: actiontrigger delete <regex trigger>" ;
}
return $ result ;
}
my $ exists = $ self - > get_trigger ( $ channel , $ trigger ) ;
if ( not defined $ exists ) { $ result = "No such trigger." ; }
else {
$ self - > delete_trigger ( $ channel , $ trigger ) ;
$ result = "Trigger deleted." ;
}
2017-08-13 08:43:36 +02:00
}
2020-02-15 23:38:32 +01:00
default {
if ( $ from !~ m/^#/ ) {
$ result =
"Usage from private message: actiontrigger list [#channel or global] | actiontrigger add <#channel or global> <level> <repeat delay (in seconds)> <regex trigger> <command> | actiontrigger delete <#channel or global> <regex trigger>" ;
} else {
$ result =
"Usage: actiontrigger list [#channel or global] | actiontrigger add <level> <repeat delay (in seconds)> <regex trigger> <command> | actiontrigger delete <regex>" ;
}
2017-08-13 08:43:36 +02:00
}
}
2020-02-15 23:38:32 +01:00
return $ result ;
2017-08-13 08:43:36 +02:00
}
1 ;