App-RoboBot

 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;



( run in 0.839 second using v1.01-cache-2.11-cpan-49f99fa48dc )