view release on metacpan or search on metacpan
lib/App/RoboBot/Macro.pm view on Meta::CPAN
);
has 'timestamp' => (
is => 'rw',
isa => 'DateTime',
traits => [qw( SetOnce )],
default => sub { DateTime->now() },
required => 1,
);
has 'is_locked' => (
is => 'rw',
isa => 'Bool',
default => 0,
);
has 'valid' => (
is => 'ro',
isa => 'Bool',
writer => '_set_valid',
);
lib/App/RoboBot/Macro.pm view on Meta::CPAN
sub load_all {
my ($class, $bot) = @_;
my $logger = $bot->logger('core.macro');
$logger->debug('Macro load_all() request.');
my $res = $bot->config->db->do(q{
select m.macro_id, m.network_id, m.name, m.arguments, m.definition,
n.name as nick, m.defined_at, m.is_locked
from macros m
join nicks n on (n.id = m.defined_by)
});
return unless $res;
my %macros;
while ($res->next) {
my $network = $bot->network_by_id($res->{'network_id'});
lib/App/RoboBot/Macro.pm view on Meta::CPAN
$macros{$network->id}{$res->{'name'}} = $class->new(
bot => $bot,
id => $res->{'macro_id'},
network => $network,
name => $res->{'name'},
arguments => decode_json($res->{'arguments'}),
definition => $res->{'definition'},
definer => App::RoboBot::Nick->new( config => $bot->config, name => $res->{'nick'} ),
timestamp => DateTime::Format::Pg->parse_datetime($res->{'defined_at'}),
is_locked => $res->{'is_locked'},
);
}
$logger->debug('All macros loaded. Returning collection.');
return %macros;
}
sub save {
my ($self) = @_;
lib/App/RoboBot/Macro.pm view on Meta::CPAN
if ($self->has_id) {
$self->log->debug(sprintf('Macro %s already has ID (%d). Updating existing record.', $self->name, $self->id));
$res = $self->bot->config->db->do(q{
update macros set ??? where macro_id = ?
}, {
name => $self->name,
arguments => encode_json($self->arguments),
definition => $self->definition,
is_locked => $self->is_locked,
}, $self->id);
return 1 if $res;
} else {
$self->log->debug(sprintf('Macro %s does not have ID. Creating new record.', $self->name));
unless ($self->has_definer) {
$self->log->error(sprintf('Attempted to save macro %s on network %s without a definer attribute.', $self->name, $self->network->name));
return 0;
}
$res = $self->bot->config->db->do(q{
insert into macros ??? returning macro_id
}, {
name => $self->name,
network_id => $self->network->id,
arguments => encode_json($self->arguments),
definition => $self->definition,
defined_by => $self->definer->id,
defined_at => $self->timestamp,
is_locked => $self->is_locked,
});
if ($res && $res->next) {
$self->log->debug(sprintf('New macro record created for %s on network %s (ID %d).', $self->name, $self->network->name, $res->{'macro_id'}));
$self->id($res->{'macro_id'});
return 1;
}
}
lib/App/RoboBot/Macro.pm view on Meta::CPAN
$self->log->debug('Record deletion successful.');
return 1;
}
sub lock {
my ($self) = @_;
$self->log->debug(sprintf('Macro lock request for %s on network %s.', $self->name, $self->network->name));
return 0 if $self->is_locked;
$self->is_locked(1);
return $self->save;
}
sub unlock {
my ($self) = @_;
$self->log->debug(sprintf('Macro unlock request for %s on network %s.', $self->name, $self->network->name));
return 0 if ! $self->is_locked;
$self->is_locked(0);
return $self->save;
}
sub expand {
my ($self, $message, $rpl, @args) = @_;
$self->log->debug(sprintf('Macro expansion for %s on network %s (%d arguments).', $self->name, $self->network->name, scalar(@args)));
my $expr = clone($self->expression);
lib/App/RoboBot/Plugin/Core/Macro.pm view on Meta::CPAN
(grep-for-foo "foo" "bar" "baz" "food")
("foo" "food")
=head2 undefmacro
=head3 Description
Undefines the named macro. This is a permanent action and if the named macro is
desired again, it must be recreated from scratch.
If the macro has been locked by its author, only they may undefine it. Anyone
else attempting to remove the macro will receive an error explaining that it is
currently locked.
=head3 Usage
<macro name>
=head2 show-macro
=head3 Description
Displays a macro's definition and who authored it.
lib/App/RoboBot/Plugin/Core/Macro.pm view on Meta::CPAN
available to the author of the macro.
=head3 Usage
<macro name>
=head2 unlock-macro
=head3 Description
Unlocks a previously locked macro, allowing it to once again be modified or
deleted. This function is only available to the author of the macro.
=head3 Usage
<macro name>
=cut
has '+commands' => (
default => sub {{
lib/App/RoboBot/Plugin/Core/Macro.pm view on Meta::CPAN
'list-macros' => { method => 'list_macros',
description => 'Displays a list of all registered macros. Optional pattern will limit list to only those macros whose names match.',
usage => '[<pattern>]', },
'lock-macro' => { method => 'lock_macro',
description => 'Locks a macro from further modification or deletion. This function is only available to the author of the macro.',
usage => '<macro name>' },
'unlock-macro' => { method => 'unlock_macro',
description => 'Unlocks a previously locked macro, allowing it to once again be modified or deleted. This function is only available to the author of the macro.',
usage => '<macro name>' },
}},
);
sub list_macros {
my ($self, $message, $command, $rpl, $pattern) = @_;
my $res = $self->bot->config->db->do(q{
select name
from macros
lib/App/RoboBot/Plugin/Core/Macro.pm view on Meta::CPAN
} else {
$macro_name = $macro_name->evaluate($message, $rpl);
}
# Enforce a few rules on macro names.
unless (defined $macro_name && !ref($macro_name) && $macro_name =~ m{^[^\s\{\(\)\[\]\{\}\|,#]+$} && substr($macro_name, 0, 1) ne "'") {
$message->response->raise('Macro name must be a single string value.');
return;
}
if (exists $self->bot->macros->{$network->id}{lc($macro_name)} && $self->bot->macros->{$network->id}{lc($macro_name)}->is_locked) {
if ($self->bot->macros->{$network->id}{lc($macro_name)}->definer->id != $message->sender->id) {
$message->response->raise(
'The %s macro has been locked by its creator (who happens to not be you) and cannot be redefined by anyone else.',
$self->bot->macros->{$network->id}{lc($macro_name)}->name
);
return;
}
}
unless (blessed($args) && ($args->type eq 'List' || $args->type eq 'Vector')) {
$message->response->raise('Macro arguments must be specified as a list or vector.');
return;
}
lib/App/RoboBot/Plugin/Core/Macro.pm view on Meta::CPAN
unless (defined $macro_name && $macro_name =~ m{\w+}o) {
$message->response->raise('Must provide the name of a macro to undefine.');
return;
}
unless (exists $self->bot->macros->{$network->id}{$macro_name}) {
$message->response->raise('Macro %s has not been defined.', $macro_name);
return;
}
if ($self->bot->macros->{$network->id}{$macro_name}->is_locked && $self->bot->macros->{$network->id}{$macro_name}->definer != $message->sender->id) {
$message->response->raise(
'The %s macro has been locked by its creator (who happens to not be you). You may not undefine it.',
$self->bot->macros->{$network->id}{$macro_name}->name
);
} else {
if ($self->bot->remove_macro($network, $macro_name)) {
$message->response->push(sprintf('Macro %s undefined.', $macro_name));
} else {
$message->response->push(sprintf('Could not undefine macro %s.', $macro_name));
}
}
lib/App/RoboBot/Plugin/Core/Macro.pm view on Meta::CPAN
$message->response->raise('No such macro defined.');
return;
}
my $macro = $self->bot->macros->{$network->id}{$macro_name};
my $pp = sprintf('(defmacro %s [%s] \'%s)', $macro->name, $macro->signature, $macro->expression->flatten);
$pp =~ s{\n\s+([^\(]+)\n}{ $1\n}gs;
$message->response->push($pp);
$message->response->push(sprintf('Defined by <%s> on %s', $macro->definer->name, $macro->timestamp->ymd));
$message->response->push('This macro is locked and may only be edited by its definer.') if $macro->is_locked;
return;
}
sub lock_macro {
my ($self, $message, $command, $rpl, $macro) = @_;
my $network = $message->network;
unless (defined $macro && $macro =~ m{\S+}) {
lib/App/RoboBot/Plugin/Core/Macro.pm view on Meta::CPAN
return;
}
unless (exists $self->bot->macros->{$network->id}{lc($macro)}) {
$message->response->raise('No such macro defined.');
return;
}
$macro = $self->bot->macros->{$network->id}{lc($macro)};
if ($macro->is_locked) {
$message->response->raise('The macro %s is already locked.', $macro->name);
return;
}
unless ($macro->definer->id == $message->sender->id) {
$message->response->raise('You did not define the %s macro and cannot lock it. You may only lock your own macros.', $macro->name);
return;
}
unless ($macro->lock(1) && $macro->save) {
$message->response->raise('Could not lock the %s macro. Please try again.', $macro->name);
return;
}
$message->response->push(sprintf('Your %s macro is now locked. Nobody but you may modify or delete it.', $macro->name));
return;
}
sub unlock_macro {
my ($self, $message, $command, $rpl, $macro) = @_;
my $network = $message->network;
unless (defined $macro && $macro =~ m{\S+}) {
$message->response->raise('Must provide the name of the macro you wish to unlock.');
return;
}
unless (exists $self->bot->macros->{$network->id}{lc($macro)}) {
$message->response->raise('No such macro defined.');
return;
}
$macro = $self->bot->macros->{$network->id}{lc($macro)};
if ( ! $macro->is_locked) {
$message->response->raise('The macro %s is not locked.', $macro->name);
return;
}
unless ($macro->definer->id == $message->sender->id) {
$message->response->raise('You did not define the %s macro and cannot unlock it. You may only unlock your own macros.', $macro->name);
return;
}
unless ($macro->lock(0) && $macro->save) {
$message->response->raise('Could not unlock the %s macro. Please try again.', $macro->name);
return;
}
$message->response->push(sprintf('Your %s macro is now unlocked. Anybody else may modify or delete it.', $macro->name));
return;
}
sub _pprint {
my ($list, $nlv) = @_;
$list //= [];
$nlv //= 1;
if ($nlv <= 1 && ref($list) eq 'ARRAY' && scalar(@{$list}) == 1) {
share/migrations/deploy/p-macros-20161128210159.sql view on Meta::CPAN
-- requires: base
BEGIN;
CREATE TABLE robobot.macros (
macro_id serial not null,
network_id integer not null references robobot.networks (id) on update cascade on delete restrict,
name text not null,
arguments jsonb not null default '{}'::jsonb,
definition text not null,
is_locked boolean not null default false,
defined_by integer not null references robobot.nicks (id) on update cascade on delete restrict,
defined_at timestamp with time zone not null default now(),
PRIMARY KEY (macro_id)
);
CREATE UNIQUE INDEX ON robobot.macros (network_id, lower(name));
CREATE INDEX ON robobot.macros (defined_by);
COMMIT;