mirror of https://github.com/pragma-/pbot.git
Progress on refactoring and polishing everything
This commit is contained in:
parent
79e7fd4d29
commit
8b093d313c
|
@ -438,6 +438,7 @@ sub interpret {
|
||||||
nick => $context->{nick},
|
nick => $context->{nick},
|
||||||
user => $context->{user},
|
user => $context->{user},
|
||||||
host => $context->{host},
|
host => $context->{host},
|
||||||
|
hostmask => $context->{hostmask},
|
||||||
command => $context->{command},
|
command => $context->{command},
|
||||||
checkflood => 1,
|
checkflood => 1,
|
||||||
message => "$context->{nick}: Why would I want to do that to myself?",
|
message => "$context->{nick}: Why would I want to do that to myself?",
|
||||||
|
@ -659,6 +660,7 @@ sub handle_result {
|
||||||
nick => $context->{nick},
|
nick => $context->{nick},
|
||||||
user => $context->{user},
|
user => $context->{user},
|
||||||
host => $context->{host},
|
host => $context->{host},
|
||||||
|
hostmask => $context->{hostmask},
|
||||||
command => $context->{command},
|
command => $context->{command},
|
||||||
message => $message,
|
message => $message,
|
||||||
checkflood => 1
|
checkflood => 1
|
||||||
|
@ -682,6 +684,7 @@ sub handle_result {
|
||||||
nick => $context->{nick},
|
nick => $context->{nick},
|
||||||
user => $context->{user},
|
user => $context->{user},
|
||||||
host => $context->{host},
|
host => $context->{host},
|
||||||
|
hostmask => $context->{hostmask},
|
||||||
command => $context->{command},
|
command => $context->{command},
|
||||||
message => $line,
|
message => $line,
|
||||||
checkflood => 1,
|
checkflood => 1,
|
||||||
|
@ -891,6 +894,7 @@ sub add_message_to_output_queue {
|
||||||
nick => $message->{nick},
|
nick => $message->{nick},
|
||||||
user => $message->{user},
|
user => $message->{user},
|
||||||
host => $message->{host},
|
host => $message->{host},
|
||||||
|
hostmask => $message->{hostmask},
|
||||||
output => $message->{message},
|
output => $message->{message},
|
||||||
command => $message->{command},
|
command => $message->{command},
|
||||||
checkflood => $message->{checkflood}
|
checkflood => $message->{checkflood}
|
||||||
|
@ -912,6 +916,7 @@ sub add_to_command_queue {
|
||||||
nick => $command->{nick},
|
nick => $command->{nick},
|
||||||
user => $command->{user},
|
user => $command->{user},
|
||||||
host => $command->{host},
|
host => $command->{host},
|
||||||
|
hostmask => $command->{hostmask},
|
||||||
command => $command->{command},
|
command => $command->{command},
|
||||||
interpret_depth => 0,
|
interpret_depth => 0,
|
||||||
checkflood => 0,
|
checkflood => 0,
|
||||||
|
@ -935,12 +940,14 @@ sub add_botcmd_to_command_queue {
|
||||||
my ($self, $channel, $command, $delay) = @_;
|
my ($self, $channel, $command, $delay) = @_;
|
||||||
|
|
||||||
my $botcmd = {
|
my $botcmd = {
|
||||||
nick => $self->{pbot}->{registry}->get_value('irc', 'botnick'),
|
nick => $self->{pbot}->{registry}->get_value('irc', 'botnick'),
|
||||||
user => 'stdin',
|
user => 'stdin',
|
||||||
host => 'pbot',
|
host => 'pbot',
|
||||||
command => $command
|
command => $command
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$botcmd->{hostmask} = "$botcmd->{nick}!stdin\@pbot";
|
||||||
|
|
||||||
$self->add_to_command_queue($channel, $botcmd, $delay);
|
$self->add_to_command_queue($channel, $botcmd, $delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ use PBot::Imports;
|
||||||
|
|
||||||
use Time::Duration qw/concise duration/;
|
use Time::Duration qw/concise duration/;
|
||||||
|
|
||||||
our $seconds ||= 0;
|
our $seconds //= 0;
|
||||||
our $waitfor ||= 1;
|
our $waitfor //= 1;
|
||||||
our @timer_funcs;
|
our @timer_funcs;
|
||||||
|
|
||||||
# alarm signal handler (poor-man's timer)
|
# alarm signal handler (poor-man's timer)
|
||||||
|
@ -204,8 +204,9 @@ sub enqueue_event_unless_exists {
|
||||||
sub enqueue_event {
|
sub enqueue_event {
|
||||||
my ($self, $ref, $interval, $id, $repeating) = @_;
|
my ($self, $ref, $interval, $id, $repeating) = @_;
|
||||||
|
|
||||||
$id ||= 'anonymous event';
|
$id //= 'anonymous event';
|
||||||
$repeating ||= 0;
|
$repeating //= 0;
|
||||||
|
$interval //= 0;
|
||||||
|
|
||||||
my $event = {
|
my $event = {
|
||||||
id => $id,
|
id => $id,
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# File: ActionTrigger.pm
|
||||||
# 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/.
|
# Purpose: provides interface to set/remove/modify regular expression triggers
|
||||||
|
|
||||||
package Plugins::ActionTrigger;
|
|
||||||
use parent 'Plugins::Plugin';
|
|
||||||
|
|
||||||
# purpose: provides interface to set/remove/modify regular expression triggers
|
|
||||||
# to execute a command.
|
# to execute a command.
|
||||||
#
|
#
|
||||||
# Usage: actiontrigger add <#channel or global> <capability> <repeat delay (in seconds)> <regex trigger> <command>
|
# Usage: actiontrigger add <#channel or global> <capability> <rate-limit (in seconds)> <regex trigger> <command>
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
#
|
#
|
||||||
|
@ -28,8 +23,13 @@ use parent 'Plugins::Plugin';
|
||||||
# actiontrigger add #channel none 0 "(?i)how is the weather (?:in|for) (.*) today" weather $1
|
# actiontrigger add #channel none 0 "(?i)how is the weather (?:in|for) (.*) today" weather $1
|
||||||
#
|
#
|
||||||
# These are basic examples; more complex examples can be crafted.
|
# These are basic examples; more complex examples can be crafted.
|
||||||
#
|
|
||||||
# TODO: share actually useful examples from personal bot
|
# 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/.
|
||||||
|
|
||||||
|
package Plugins::ActionTrigger;
|
||||||
|
use parent 'Plugins::Plugin';
|
||||||
|
|
||||||
use PBot::Imports;
|
use PBot::Imports;
|
||||||
|
|
||||||
|
@ -39,9 +39,14 @@ use Time::HiRes qw/gettimeofday/;
|
||||||
|
|
||||||
sub initialize {
|
sub initialize {
|
||||||
my ($self, %conf) = @_;
|
my ($self, %conf) = @_;
|
||||||
|
|
||||||
|
# register bot command
|
||||||
$self->{pbot}->{commands}->register(sub { $self->cmd_actiontrigger(@_) }, 'actiontrigger', 1);
|
$self->{pbot}->{commands}->register(sub { $self->cmd_actiontrigger(@_) }, 'actiontrigger', 1);
|
||||||
|
|
||||||
|
# add capability to admin group
|
||||||
$self->{pbot}->{capabilities}->add('admin', 'can-actiontrigger', 1);
|
$self->{pbot}->{capabilities}->add('admin', 'can-actiontrigger', 1);
|
||||||
|
|
||||||
|
# register IRC handlers
|
||||||
$self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_public(@_) });
|
$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.caction', sub { $self->on_action(@_) });
|
||||||
$self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) });
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) });
|
||||||
|
@ -49,18 +54,27 @@ sub initialize {
|
||||||
$self->{pbot}->{event_dispatcher}->register_handler('irc.quit', 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(@_) });
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.kick', sub { $self->on_kick(@_) });
|
||||||
|
|
||||||
|
# database file
|
||||||
$self->{filename} = $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/triggers.sqlite3';
|
$self->{filename} = $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/triggers.sqlite3';
|
||||||
|
|
||||||
|
# open and initialize database
|
||||||
$self->dbi_begin;
|
$self->dbi_begin;
|
||||||
$self->create_database;
|
$self->create_database;
|
||||||
$self->update_database;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub unload {
|
sub unload {
|
||||||
my $self = shift;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
# close database
|
||||||
$self->dbi_end;
|
$self->dbi_end;
|
||||||
|
|
||||||
|
# unregister bot command
|
||||||
$self->{pbot}->{commands}->unregister('actiontrigger');
|
$self->{pbot}->{commands}->unregister('actiontrigger');
|
||||||
|
|
||||||
|
# remove capability
|
||||||
$self->{pbot}->{capabilities}->remove('can-actiontrigger');
|
$self->{pbot}->{capabilities}->remove('can-actiontrigger');
|
||||||
|
|
||||||
|
# remove IRC handlers
|
||||||
$self->{pbot}->{event_dispatcher}->remove_handler('irc.public');
|
$self->{pbot}->{event_dispatcher}->remove_handler('irc.public');
|
||||||
$self->{pbot}->{event_dispatcher}->remove_handler('irc.caction');
|
$self->{pbot}->{event_dispatcher}->remove_handler('irc.caction');
|
||||||
$self->{pbot}->{event_dispatcher}->remove_handler('irc.join');
|
$self->{pbot}->{event_dispatcher}->remove_handler('irc.join');
|
||||||
|
@ -69,8 +83,168 @@ sub unload {
|
||||||
$self->{pbot}->{event_dispatcher}->remove_handler('irc.kick');
|
$self->{pbot}->{event_dispatcher}->remove_handler('irc.kick');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub create_database {
|
sub create_database {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
return if not $self->{dbh};
|
return if not $self->{dbh};
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
|
@ -81,7 +255,7 @@ CREATE TABLE IF NOT EXISTS Triggers (
|
||||||
action TEXT,
|
action TEXT,
|
||||||
owner TEXT,
|
owner TEXT,
|
||||||
cap_override TEXT,
|
cap_override TEXT,
|
||||||
repeatdelay INTEGER,
|
ratelimit INTEGER,
|
||||||
lastused NUMERIC
|
lastused NUMERIC
|
||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
@ -90,30 +264,9 @@ SQL
|
||||||
$self->{pbot}->{logger}->log("ActionTrigger create database failed: $@") if $@;
|
$self->{pbot}->{logger}->log("ActionTrigger create database failed: $@") if $@;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub update_database {
|
|
||||||
my $self = shift;
|
|
||||||
return if not $self->{dbh};
|
|
||||||
|
|
||||||
my %columns = ();
|
|
||||||
|
|
||||||
eval {
|
|
||||||
foreach my $col (@{$self->{dbh}->selectall_arrayref("PRAGMA TABLE_INFO(Triggers)")}) {
|
|
||||||
$columns{$col->[1]} = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not exists $columns{cap_override}) {
|
|
||||||
$self->{dbh}->do("ALTER TABLE Triggers ADD COLUMN cap_override TEXT");
|
|
||||||
$self->{dbh}->do("UPDATE Triggers SET cap_override='botowner', level=0 WHERE level >= 90");
|
|
||||||
$self->{dbh}->do("UPDATE Triggers SET cap_override='admin', level=0 WHERE level >= 60");
|
|
||||||
$self->{dbh}->do("UPDATE Triggers SET cap_override='chanop', level=0 WHERE level >= 10");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$self->{pbot}->{logger}->log("ActionTrigger update database failed: $@") if $@;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub dbi_begin {
|
sub dbi_begin {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
$self->{dbh} = DBI->connect("dbi:SQLite:dbname=$self->{filename}", "", "", {RaiseError => 1, PrintError => 0, AutoInactiveDestroy => 1, sqlite_unicode => 1})
|
$self->{dbh} = DBI->connect("dbi:SQLite:dbname=$self->{filename}", "", "", {RaiseError => 1, PrintError => 0, AutoInactiveDestroy => 1, sqlite_unicode => 1})
|
||||||
or die $DBI::errstr;
|
or die $DBI::errstr;
|
||||||
|
@ -135,19 +288,20 @@ sub dbi_end {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub add_trigger {
|
sub add_trigger {
|
||||||
my ($self, $channel, $trigger, $action, $owner, $cap_override, $repeatdelay) = @_;
|
my ($self, $channel, $trigger, $action, $owner, $cap_override, $ratelimit) = @_;
|
||||||
|
|
||||||
return 0 if $self->get_trigger($channel, $trigger);
|
return 0 if $self->get_trigger($channel, $trigger);
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
my $sth = $self->{dbh}->prepare('INSERT INTO Triggers (channel, trigger, action, owner, cap_override, repeatdelay, lastused) VALUES (?, ?, ?, ?, ?, ?, 0)');
|
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, $repeatdelay);
|
$sth->execute(lc $channel, $trigger, $action, $owner, $cap_override, $ratelimit);
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$self->{pbot}->{logger}->log("Add trigger failed: $@");
|
$self->{pbot}->{logger}->log("Add trigger failed: $@");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,11 +320,12 @@ sub list_triggers {
|
||||||
my $sth;
|
my $sth;
|
||||||
|
|
||||||
if ($channel eq '*') {
|
if ($channel eq '*') {
|
||||||
$sth = $self->{dbh}->prepare('SELECT * FROM Triggers WHERE channel != ?');
|
|
||||||
$channel = 'global';
|
$channel = 'global';
|
||||||
|
$sth = $self->{dbh}->prepare('SELECT * FROM Triggers WHERE channel != ?');
|
||||||
} else {
|
} else {
|
||||||
$sth = $self->{dbh}->prepare('SELECT * FROM Triggers WHERE channel = ?');
|
$sth = $self->{dbh}->prepare('SELECT * FROM Triggers WHERE channel = ?');
|
||||||
}
|
}
|
||||||
|
|
||||||
$sth->execute(lc $channel);
|
$sth->execute(lc $channel);
|
||||||
return $sth->fetchall_arrayref({});
|
return $sth->fetchall_arrayref({});
|
||||||
};
|
};
|
||||||
|
@ -187,20 +342,22 @@ sub update_trigger {
|
||||||
eval {
|
eval {
|
||||||
my $sql = 'UPDATE Triggers SET ';
|
my $sql = 'UPDATE Triggers SET ';
|
||||||
|
|
||||||
my $comma = '';
|
my @triggers;
|
||||||
foreach my $key (keys %$data) {
|
foreach my $key (keys %$data) {
|
||||||
$sql .= "$comma$key = ?";
|
push @triggers, "$key = ?";
|
||||||
$comma = ", ";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sql .= join ', ', @triggers;
|
||||||
$sql .= "WHERE trigger = ? AND channel = ?";
|
$sql .= "WHERE trigger = ? AND channel = ?";
|
||||||
my $sth = $self->{dbh}->prepare($sql);
|
|
||||||
|
my $sth = $self->{dbh}->prepare($sql);
|
||||||
|
|
||||||
my $param = 1;
|
my $param = 1;
|
||||||
foreach my $key (keys %$data) { $sth->bind_param($param++, $data->{$key}); }
|
foreach my $key (keys %$data) { $sth->bind_param($param++, $data->{$key}); }
|
||||||
|
|
||||||
$sth->bind_param($param++, $trigger);
|
$sth->bind_param($param++, $trigger);
|
||||||
$sth->bind_param($param, $channel);
|
$sth->bind_param($param, $channel);
|
||||||
$sth->execute();
|
$sth->execute;
|
||||||
};
|
};
|
||||||
|
|
||||||
$self->{pbot}->{logger}->log("Update trigger $channel/$trigger failed: $@\n") if $@;
|
$self->{pbot}->{logger}->log("Update trigger $channel/$trigger failed: $@\n") if $@;
|
||||||
|
@ -212,7 +369,7 @@ sub get_trigger {
|
||||||
my $row = eval {
|
my $row = eval {
|
||||||
my $sth = $self->{dbh}->prepare('SELECT * FROM Triggers WHERE channel = ? AND trigger = ?');
|
my $sth = $self->{dbh}->prepare('SELECT * FROM Triggers WHERE channel = ? AND trigger = ?');
|
||||||
$sth->execute(lc $channel, $trigger);
|
$sth->execute(lc $channel, $trigger);
|
||||||
my $row = $sth->fetchrow_hashref();
|
my $row = $sth->fetchrow_hashref;
|
||||||
return $row;
|
return $row;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -226,53 +383,99 @@ sub get_trigger {
|
||||||
|
|
||||||
sub on_kick {
|
sub on_kick {
|
||||||
my ($self, $event_type, $event) = @_;
|
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]);
|
# don't handle this event if it was caused by a bot command
|
||||||
my $channel = $event->{event}->{args}[0];
|
|
||||||
return 0 if $event->{interpreted};
|
return 0 if $event->{interpreted};
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
$self->check_trigger($nick, $user, $host, $channel, "KICK $victim $reason");
|
$self->check_trigger($nick, $user, $host, $channel, "KICK $victim $reason");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_action {
|
sub on_action {
|
||||||
my ($self, $event_type, $event) = @_;
|
my ($self, $event_type, $event) = @_;
|
||||||
my ($nick, $user, $host, $msg) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->args);
|
|
||||||
|
my ($nick, $user, $host, $msg) = (
|
||||||
|
$event->{event}->nick,
|
||||||
|
$event->{event}->user,
|
||||||
|
$event->{event}->host,
|
||||||
|
$event->{event}->args
|
||||||
|
);
|
||||||
|
|
||||||
my $channel = $event->{event}->{to}[0];
|
my $channel = $event->{event}->{to}[0];
|
||||||
#return 0 if $event->{interpreted};
|
|
||||||
$msg =~ s/^\/me\s+//;
|
$msg =~ s/^\/me\s+//;
|
||||||
|
|
||||||
$self->check_trigger($nick, $user, $host, $channel, "ACTION $msg");
|
$self->check_trigger($nick, $user, $host, $channel, "ACTION $msg");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_public {
|
sub on_public {
|
||||||
my ($self, $event_type, $event) = @_;
|
my ($self, $event_type, $event) = @_;
|
||||||
my ($nick, $user, $host, $msg) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->args);
|
|
||||||
|
my ($nick, $user, $host, $msg) = (
|
||||||
|
$event->{event}->nick,
|
||||||
|
$event->{event}->user,
|
||||||
|
$event->{event}->host,
|
||||||
|
$event->{event}->args);
|
||||||
|
|
||||||
my $channel = $event->{event}->{to}[0];
|
my $channel = $event->{event}->{to}[0];
|
||||||
#return 0 if $event->{interpreted};
|
|
||||||
$self->check_trigger($nick, $user, $host, $channel, "PRIVMSG $msg");
|
$self->check_trigger($nick, $user, $host, $channel, "PRIVMSG $msg");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_join {
|
sub on_join {
|
||||||
my ($self, $event_type, $event) = @_;
|
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;
|
my ($nick, $user, $host, $channel, $args) = (
|
||||||
|
$event->{event}->nick,
|
||||||
|
$event->{event}->user,
|
||||||
|
$event->{event}->host,
|
||||||
|
$event->{event}->to,
|
||||||
|
$event->{event}->args
|
||||||
|
);
|
||||||
|
|
||||||
$self->check_trigger($nick, $user, $host, $channel, "JOIN");
|
$self->check_trigger($nick, $user, $host, $channel, "JOIN");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_departure {
|
sub on_departure {
|
||||||
my ($self, $event_type, $event) = @_;
|
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;
|
my ($nick, $user, $host, $channel, $args) = (
|
||||||
|
$event->{event}->nick,
|
||||||
|
$event->{event}->user,
|
||||||
|
$event->{event}->host,
|
||||||
|
$event->{event}->to,
|
||||||
|
$event->{event}->args
|
||||||
|
);
|
||||||
|
|
||||||
$self->check_trigger($nick, $user, $host, $channel, (uc $event->{event}->type) . " $args");
|
$self->check_trigger($nick, $user, $host, $channel, (uc $event->{event}->type) . " $args");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_trigger {
|
sub check_trigger {
|
||||||
my ($self, $nick, $user, $host, $channel, $text) = @_;
|
my ($self, $nick, $user, $host, $channel, $text) = @_;
|
||||||
|
|
||||||
|
# database not available
|
||||||
return 0 if not $self->{dbh};
|
return 0 if not $self->{dbh};
|
||||||
|
|
||||||
|
$channel = lc $channel;
|
||||||
|
|
||||||
|
# TODO: cache these instead of loading them again every message
|
||||||
my @triggers = $self->list_triggers($channel);
|
my @triggers = $self->list_triggers($channel);
|
||||||
my @globals = $self->list_triggers('global');
|
my @globals = $self->list_triggers('global');
|
||||||
push @triggers, @globals;
|
push @triggers, @globals;
|
||||||
|
@ -282,160 +485,46 @@ sub check_trigger {
|
||||||
|
|
||||||
foreach my $trigger (@triggers) {
|
foreach my $trigger (@triggers) {
|
||||||
eval {
|
eval {
|
||||||
$trigger->{lastused} = 0 if not defined $trigger->{lastused};
|
$trigger->{lastused} //= 0;
|
||||||
$trigger->{repeatdelay} = 0 if not defined $trigger->{repeatdelay};
|
$trigger->{ratelimit} //= 0;
|
||||||
if ($now - $trigger->{lastused} >= $trigger->{repeatdelay} and $text =~ m/$trigger->{trigger}/) {
|
|
||||||
|
if ($now - $trigger->{lastused} >= $trigger->{ratelimit} and $text =~ m/$trigger->{trigger}/) {
|
||||||
|
my @stuff = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
|
||||||
|
|
||||||
$trigger->{lastused} = $now;
|
$trigger->{lastused} = $now;
|
||||||
my $data = {lastused => $now};
|
|
||||||
$self->update_trigger($trigger->{channel}, $trigger->{trigger}, $data);
|
$self->update_trigger($trigger->{channel}, $trigger->{trigger}, { lastused => $now });
|
||||||
|
|
||||||
my $action = $trigger->{action};
|
my $action = $trigger->{action};
|
||||||
my @stuff = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
|
|
||||||
my $i;
|
my $i;
|
||||||
map { ++$i; $action =~ s/\$$i/$_/g; } @stuff;
|
map { ++$i; $action =~ s/\$$i/$_/g; } @stuff;
|
||||||
|
|
||||||
my $delay = 0;
|
|
||||||
|
|
||||||
my ($n, $u, $h) = $trigger->{owner} =~ /^([^!]+)!([^@]+)\@(.*)$/;
|
my ($n, $u, $h) = $trigger->{owner} =~ /^([^!]+)!([^@]+)\@(.*)$/;
|
||||||
|
|
||||||
my $command = {
|
my $command = {
|
||||||
nick => $n,
|
nick => $n,
|
||||||
user => $u,
|
user => $u,
|
||||||
host => $h,
|
host => $h,
|
||||||
command => $action,
|
hostmask => "$n!$u\@$host",
|
||||||
|
command => $action,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (length $trigger->{cap_override} and $trigger->{cap_override} ne 'none') {
|
if ($trigger->{cap_override} and $trigger->{cap_override} ne 'none') {
|
||||||
$command->{'cap-override'} = $trigger->{cap_override};
|
$command->{'cap-override'} = $trigger->{cap_override};
|
||||||
}
|
}
|
||||||
|
|
||||||
my $cap = '';
|
my $cap = '';
|
||||||
$cap = " (capability=$command->{'cap-override'})" if exists $command->{'cap-override'};
|
$cap = " (capability=$command->{'cap-override'})" if exists $command->{'cap-override'};
|
||||||
$self->{pbot}->{logger}->log("ActionTrigger: ($channel) $trigger->{trigger} -> $action$cap\n");
|
$self->{pbot}->{logger}->log("ActionTrigger: ($channel) $trigger->{trigger} -> $action$cap\n");
|
||||||
$self->{pbot}->{interpreter}->add_to_command_queue($channel, $command, $delay);
|
|
||||||
|
$self->{pbot}->{interpreter}->add_to_command_queue($channel, $command);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($@) { $self->{pbot}->{logger}->log("Skipping bad trigger $trigger->{trigger}: $@"); }
|
if ($@) { $self->{pbot}->{logger}->log("Skipping bad trigger $trigger->{trigger}: $@"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cmd_actiontrigger {
|
|
||||||
my ($self, $context) = @_;
|
|
||||||
return "Internal error." if not $self->{dbh};
|
|
||||||
|
|
||||||
my $command = $self->{pbot}->{interpreter}->shift_arg($context->{arglist});
|
|
||||||
my $result;
|
|
||||||
given ($command) {
|
|
||||||
when ('list') {
|
|
||||||
my $channel = $self->{pbot}->{interpreter}->shift_arg($context->{arglist});
|
|
||||||
if (not defined $channel) {
|
|
||||||
if ($context->{from} !~ /^#/) { $channel = 'global'; }
|
|
||||||
else { $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) { $result = "No action triggers set for $channel."; }
|
|
||||||
else {
|
|
||||||
$result = "Triggers for $channel:\n";
|
|
||||||
my $comma = '';
|
|
||||||
foreach my $trigger (@triggers) {
|
|
||||||
$trigger->{cap_override} //= 'none';
|
|
||||||
$trigger->{repeatdelay} //= 0;
|
|
||||||
$result .= "$comma$trigger->{trigger} -> $trigger->{action}";
|
|
||||||
$result .= " (capability=$trigger->{cap_override})" if length $trigger->{cap_override} and $trigger->{cap_override} ne 'none';
|
|
||||||
$result .= " (repeatdelay=$trigger->{repeatdelay})" if $trigger->{repeatdelay} != 0;
|
|
||||||
$comma = ",\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO: use GetOpt flags instead of positional arguments
|
|
||||||
when ('add') {
|
|
||||||
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> <repeat delay (in seconds)> <regex trigger> <command>";
|
|
||||||
} elsif ($channel !~ m/^#/ and $channel ne 'global') {
|
|
||||||
return "Invalid channel $channel. Usage: actiontrigger add <#channel or global> <capability> <repeat delay (in seconds)> <regex trigger> <command>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my ($cap_override, $repeatdelay, $trigger, $action) = $self->{pbot}->{interpreter}->split_args($context->{arglist}, 4, 0, 1);
|
|
||||||
|
|
||||||
if (not defined $trigger or not defined $action) {
|
|
||||||
if ($context->{from} !~ m/^#/) {
|
|
||||||
$result =
|
|
||||||
"To use this command from private message the <channel> argument is required. Usage: actiontrigger add <#channel or global> <capability> <repeat delay (in seconds)> <regex trigger> <command>";
|
|
||||||
} else {
|
|
||||||
$result = "Usage: actiontrigger add <capability> <repeat delay (in seconds)> <regex trigger> <command>";
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $exists = $self->get_trigger($channel, $trigger);
|
|
||||||
|
|
||||||
if (defined $exists) { return "Trigger already exists."; }
|
|
||||||
|
|
||||||
if ($repeatdelay !~ m/^\d+$/) { return "$context->{nick}: Missing repeat delay 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, $repeatdelay)) { $result = "Trigger added."; }
|
|
||||||
else { $result = "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/^#/) {
|
|
||||||
$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.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default {
|
|
||||||
if ($context->{from} !~ m/^#/) {
|
|
||||||
$result =
|
|
||||||
"Usage from private message: actiontrigger list [#channel or global] | actiontrigger add <#channel or global> <capability> <repeat delay (in seconds)> <regex trigger> <command> | actiontrigger delete <#channel or global> <regex trigger>";
|
|
||||||
} else {
|
|
||||||
$result =
|
|
||||||
"Usage: actiontrigger list [#channel or global] | actiontrigger add <capability> <repeat delay (in seconds)> <regex trigger> <command> | actiontrigger delete <regex>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -375,8 +375,13 @@ sub send_message {
|
||||||
$delay = 0 if not defined $delay;
|
$delay = 0 if not defined $delay;
|
||||||
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
||||||
my $message = {
|
my $message = {
|
||||||
nick => $botnick, user => 'battleship', host => 'localhost', command => 'battleship text', checkflood => 1,
|
nick => $botnick,
|
||||||
message => $text
|
user => 'battleship',
|
||||||
|
host => 'localhost',
|
||||||
|
hostmask => "$botnick!battleship\@localhost",
|
||||||
|
command => 'battleship',
|
||||||
|
checkflood => 1,
|
||||||
|
message => $text
|
||||||
};
|
};
|
||||||
$self->{pbot}->{interpreter}->add_message_to_output_queue($to, $message, $delay);
|
$self->{pbot}->{interpreter}->add_message_to_output_queue($to, $message, $delay);
|
||||||
}
|
}
|
||||||
|
|
|
@ -364,8 +364,13 @@ sub send_message {
|
||||||
$delay = 0 if not defined $delay;
|
$delay = 0 if not defined $delay;
|
||||||
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
||||||
my $message = {
|
my $message = {
|
||||||
nick => $botnick, user => 'connect4', host => 'localhost', command => 'connect4 text', checkflood => 1,
|
nick => $botnick,
|
||||||
message => $text
|
user => 'connect4',
|
||||||
|
host => 'localhost',
|
||||||
|
hostmask => "$botnick!connect4\@localhost",
|
||||||
|
command => 'connect4',
|
||||||
|
checkflood => 1,
|
||||||
|
message => $text
|
||||||
};
|
};
|
||||||
$self->{pbot}->{interpreter}->add_message_to_output_queue($to, $message, $delay);
|
$self->{pbot}->{interpreter}->add_message_to_output_queue($to, $message, $delay);
|
||||||
}
|
}
|
||||||
|
|
|
@ -871,8 +871,13 @@ sub send_message {
|
||||||
$delay = 0 if not defined $delay;
|
$delay = 0 if not defined $delay;
|
||||||
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
||||||
my $message = {
|
my $message = {
|
||||||
nick => $botnick, user => 'spinach', host => 'localhost', command => 'spinach text', checkflood => 1,
|
nick => $botnick,
|
||||||
message => $text
|
user => 'spinach',
|
||||||
|
host => 'localhost',
|
||||||
|
hostmask => "$botnick!spinach\@localhost",
|
||||||
|
command => 'spinach',
|
||||||
|
checkflood => 1,
|
||||||
|
message => $text
|
||||||
};
|
};
|
||||||
$self->{pbot}->{interpreter}->add_message_to_output_queue($to, $message, $delay);
|
$self->{pbot}->{interpreter}->add_message_to_output_queue($to, $message, $delay);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue