From 4f1948e0fe10075464732db5cc2168207c00c7aa Mon Sep 17 00:00:00 2001 From: Michael Andreen Date: Sat, 25 Apr 2009 23:16:22 +0200 Subject: [PATCH] Basic infrastructure for new irc command dispatcher The new dispatcher works like Controllers in the Catalyst framework. New commands are added as simple subroutines in the NDIRC/Commands/. The Basic module contains commands that all the bots share. --- Command.pm | 61 +++++++++++++++++++ Commands/Basic.pm | 63 +++++++++++++++++++ Context.pm | 121 +++++++++++++++++++++++++++++++++++++ Dispatcher.pm | 151 ++++++++++++++++++++++++++++++++++++++++++++++ Misc.pm | 95 ++++++++++------------------- ndawn.pl | 89 +++++++++++++++++++++++++++ 6 files changed, 516 insertions(+), 64 deletions(-) create mode 100644 Command.pm create mode 100644 Commands/Basic.pm create mode 100644 Context.pm create mode 100644 Dispatcher.pm create mode 100644 ndawn.pl diff --git a/Command.pm b/Command.pm new file mode 100644 index 0000000..e38ba13 --- /dev/null +++ b/Command.pm @@ -0,0 +1,61 @@ +#************************************************************************** +# Copyright (C) 2009 by Michael Andreen * +# * +# This program is free software; you can redistribute it and/or modify * +# it under the terms of the GNU General Public License as published by * +# the Free Software Foundation; either version 2 of the License, or * +# (at your option) any later version. * +# * +# This program is distributed in the hope that it will be useful, * +# but WITHOUT ANY WARRANTY; without even the implied warranty of * +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# GNU General Public License for more details. * +# * +# You should have received a copy of the GNU General Public License * +# along with this program; if not, write to the * +# Free Software Foundation, Inc., * +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * +#**************************************************************************/ + +package NDIRC::Command; +use strict; +use warnings; + +use Moose; + +has acl => ( + is => 'ro', + isa => 'ArrayRef[Str]', + predicate => 'has_acl', +); + +has type => ( + is => 'ro', + isa => 'ArrayRef[Str]', + default => sub { ['pub'] }, +); + +has help => ( + is => 'ro', + isa => 'ArrayRef[Str]', + default => sub{ ['No help for this command'] }, +); + +has name => ( + is => 'ro', + isa => 'Str', + default => 'No help for this command', +); + +has func => ( + is => 'ro', + isa => 'CodeRef', + required => 1, +); + +sub execute { + my ($self,$c,$args) = @_; + $self->func->(@_); +} + +1; diff --git a/Commands/Basic.pm b/Commands/Basic.pm new file mode 100644 index 0000000..f040487 --- /dev/null +++ b/Commands/Basic.pm @@ -0,0 +1,63 @@ +#************************************************************************** +# Copyright (C) 2008 by Michael Andreen * +# * +# This program is free software; you can redistribute it and/or modify * +# it under the terms of the GNU General Public License as published by * +# the Free Software Foundation; either version 2 of the License, or * +# (at your option) any later version. * +# * +# This program is distributed in the hope that it will be useful, * +# but WITHOUT ANY WARRANTY; without even the implied warranty of * +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# GNU General Public License for more details. * +# * +# You should have received a copy of the GNU General Public License * +# along with this program; if not, write to the * +# Free Software Foundation, Inc., * +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * +#**************************************************************************/ + +package NDIRC::Commands::Basic; + +use strict; +use warnings; + +use Moose; +use MooseX::MethodAttributes; + +sub commands + : Help(commands | Gives help about all available commands or lists all commands available in the current channel) +{ + my ($self, $c, $command) = @_; + + unless($command){ + my @commands; + for (sort keys %{$c->disp->commands}){ + next unless $c->disp->has_command($_,$c->channel); + my $acl = $c->disp->commands->{$_}->acl; + next if $acl && !$c->check_user_roles(@$acl); + + push @commands,$_; + } + $c->reply(join ', ', @commands); + }elsif (exists $c->disp->commands->{$command}){ + for (@{$c->disp->commands->{$command}->help}){ + $c->reply($_); + } + } + +} + +sub help + : Help(Prints basic help) + : Type(help) +{ + my ($self, $c, $command) = @_; + + $c->reply("Use .commands to show help about a specific command. " + . "Use .commands to list the diffenrent commands you have access to in this channel. " + . "Instead of . you can use ! to get reply in pm or ~ to get reply in channel."); +} + +1; + diff --git a/Context.pm b/Context.pm new file mode 100644 index 0000000..5d0fb1c --- /dev/null +++ b/Context.pm @@ -0,0 +1,121 @@ +#************************************************************************** +# Copyright (C) 2009 by Michael Andreen * +# * +# This program is free software; you can redistribute it and/or modify * +# it under the terms of the GNU General Public License as published by * +# the Free Software Foundation; either version 2 of the License, or * +# (at your option) any later version. * +# * +# This program is distributed in the hope that it will be useful, * +# but WITHOUT ANY WARRANTY; without even the implied warranty of * +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# GNU General Public License for more details. * +# * +# You should have received a copy of the GNU General Public License * +# along with this program; if not, write to the * +# Free Software Foundation, Inc., * +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * +#**************************************************************************/ + +package NDIRC::Context; +use strict; +use warnings; +use feature ':5.10'; + +use Moose; + +use Set::Object (); + +has host => ( + is => 'ro', + isa => 'Str', + required => 1 +); + +has nick => ( + is => 'ro', + isa => 'Str', + required => 1 +); + +has channel => ( + is => 'ro', + isa => 'Str', + required => 1 +); + +has roles => ( + is => 'ro', + isa => 'Object', + lazy_build => 1 +); + +has disp => ( + is => 'ro', + isa => 'Object', + required => 1 +); + +has model => ( + is => 'ro', + isa => 'Object', + required => 1 +); + +has server => ( + is => 'ro', + isa => 'Object', + required => 1 +); + +has reply_string => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +sub assert_user_roles { + my ($self,@roles) = @_; + return 1 unless @roles; + + my $need = Set::Object->new(@roles); + + if ($self->roles->superset($need)){ + return 1; + } + + die "Access denied"; +} + +sub check_user_roles { + my ($self,@roles) = @_; + + local $@; + eval { $self->assert_user_roles(@roles) }; +} + +sub reply { + my ($self,$msg) = @_; + + $self->server->command($self->reply_string . $msg); +} + +sub _build_roles { + my ($self) = @_; + + my $query = $self->model->prepare(q{ +SELECT role FROM group_roles +WHERE gid IN (SELECT gid FROM groupmembers JOIN users USING (uid) + WHERE hostmask ILIKE $1) + }); + $query->execute($self->host); + + my @roles; + while (my $group = $query->fetchrow_hashref){ + push @roles,$group->{role}; + } + return Set::Object->new(@roles); +} + + +1; diff --git a/Dispatcher.pm b/Dispatcher.pm new file mode 100644 index 0000000..5da34bd --- /dev/null +++ b/Dispatcher.pm @@ -0,0 +1,151 @@ +#************************************************************************** +# Copyright (C) 2009 by Michael Andreen * +# * +# This program is free software; you can redistribute it and/or modify * +# it under the terms of the GNU General Public License as published by * +# the Free Software Foundation; either version 2 of the License, or * +# (at your option) any later version. * +# * +# This program is distributed in the hope that it will be useful, * +# but WITHOUT ANY WARRANTY; without even the implied warranty of * +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# GNU General Public License for more details. * +# * +# You should have received a copy of the GNU General Public License * +# along with this program; if not, write to the * +# Free Software Foundation, Inc., * +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * +#**************************************************************************/ + +package NDIRC::Dispatcher; +use strict; +use warnings; +use feature ':5.10'; + +use Moose; +use Symbol; +use Set::Object (); + +use NDIRC::Command; + + +has commands => ( + is => 'rw', + isa => 'HashRef[Object]', + default => sub{ {} }, +); + +has channels => ( + is => 'rw', + isa => 'HashRef[ArrayRef[Str]]', + default => sub{ {} }, +); + +sub load { + my $self = shift; + + for (@_){ + my $class = "NDIRC::Commands::$_"; + my $file = "NDIRC/Commands/$_.pm"; + Symbol::delete_package($class); + + unless (my $return = do $file){ + warn "couldn't parse $file: $@" if $@; + warn "couldn't do $file: $!" if $!; + warn "couldn't run $file" unless $return; + }else { + print "Loading $class\n"; + $self->load_class($class); + + } + } +} + +sub load_class { + my ($self,$class) = @_; + + my @subs = eval "$class->meta->get_method_list"; + for my $c (@subs){ + my $attr = eval "$class->meta->get_method('$c')->attributes"; + if ($attr){ + print "Command: $c"; + my %c = (channels => ['all']); + my @aliases; + for (@{$attr}){ + #Parse attributes, from Attribute::Handlers + my ($attr, $data) = /^([a-z_]\w*)(?:[(](.*)[)])?$/is or next; + my $evaled = eval("package Temp; no warnings; + local \$SIG{__WARN__}=sub{die}; [$data]"); + $evaled = ($evaled && $data =~ /^\s*\[/) ? [$evaled] + : ($evaled) ? $evaled : [$data]; + + given ($attr){ + when ('Alias'){ + @aliases = @$evaled; + } + default { + $c{lc $attr} = $evaled; + } + } + } + push @aliases, $c; + my $f = eval "\\&${class}::$c"; + $c{func} = $f; + for my $a (@aliases){ + $c{name} = $a; + my $co = NDIRC::Command->new(\%c); + $self->commands->{$a} = $co; + } + } + } +} + +sub add_channel { + my ($self,$channel,$types) = @_; + + $self->channels->{lc $channel} = $types; +} + +sub has_command { + my ($self,$command,$channel) = @_; + $channel = lc $channel; + + return 0 unless exists $self->commands->{$command}; + + my $types = Set::Object->new(@{$self->commands->{$command}->type}); + my @types = qw/pub/; + @types = @{$self->channels->{$channel}} if exists $self->channels->{$channel}; + + for (@types){ + return 1 if $types->element($_); + } + return 0; +} + +sub run_command { + my ($self,$c,$command,$args) = @_; + + return 0 unless exists $self->commands->{$command}; + my $acl = $self->commands->{$command}->acl; + return 0 if $acl && !$c->check_user_roles(@$acl); + + $args //= ''; + + eval { + $self->commands->{$command}->execute($c,$args) + }; + if ($@){ + given ($@){ + when(/^ARGS/){ + for (@{$c->disp->commands->{$command}->help}){ + $c->reply($_); + } + } + default { + $c->reply("Something went wrong. If it is not temporary, please report it. '$_'"); + } + } + } +} + +1; diff --git a/Misc.pm b/Misc.pm index 64890ad..74159ac 100644 --- a/Misc.pm +++ b/Misc.pm @@ -19,34 +19,15 @@ package NDIRC::Misc; use strict; use warnings; -require Exporter; - -our @ISA = qw/Exporter/; +use feature ':5.10'; -our @EXPORT = qw/valuecolor addCommand parseCommand commands/; +require Exporter; -our %channels; -our %commands; +use NDIRC::Context; -$channels{'#def-ndawn'} = ['all','member','def']; -$channels{'#nd'} = ['all','member']; -$channels{'#ndef'} = ['all','member','scan']; -$channels{'#nd-day'} = ['all','member']; -$channels{'#ndintel'} = ['all','member']; -$channels{'#nd-officers'} = ['all','member']; -$channels{'#nd-seniors'} = ['all','member']; -$channels{'#nd-ia'} = ['all','member']; -$channels{'#ndawn'} = ['all']; -$channels{'pm'} = ['pm']; +our @ISA = qw/Exporter/; -$ND::defchan = "#def-ndawn"; -$ND::memchan = "#nd"; -$ND::scanchan = "#ndef"; -$ND::bcchan = "#nd-day"; -$ND::intelchan = "#ndintel"; -$ND::officerchan = "#nd-officers"; -$ND::communitychan = "#ndawn"; -$ND::pubchan = "#newdawn"; +our @EXPORT = qw/valuecolor parseCommand/; sub valuecolor { my $s = $_; @@ -59,52 +40,38 @@ sub valuecolor { return $s; } -sub addCommand { - my ($command, $list, $func) = @_; - $commands{$command} = {fun => $func, acc => $list}; -} - sub parseCommand { - my ($msg,$channel) = @_; - if ($msg =~ /^(\S+)(?: (.+))?$/){ - my $c = $1; - my $args = $2; - my @k = keys %commands; - if (exists $commands{$c}){ - my $a = $commands{$c}->{acc}; - my $b = (exists $channels{lc $channel} ? $channels{lc $channel} : ['all']); - if (intersect($a,$b) > 0){ - $commands{$c}->{fun}->($args,$c); - return 1; - } - } - } - return 0; -} + my ($msg, $server, $nick, $address, $channel, $disp,$model) = @_; + + my ($p,$command,$args) = ($msg =~ /^([.!~])(\S+)(?: (.+))?/) or return 0; + + return 0 unless $disp->has_command($command,$channel); -sub commands { - my ($channel) = @_; - my @commands; - my $b = (exists $channels{lc $channel} ? $channels{lc $channel} : ['all']); - for my $c (sort keys %commands){ - my $a = $commands{$c}->{acc}; - if (intersect($a,$b) > 0){ - push @commands, $c; + my $reply_string; + given ($p){ + when ('!'){ + $reply_string = "msg $nick "; + } + when ('~'){ + $reply_string = "msg $channel "; + } + default { + $reply_string = "notice $nick "; } } - return join ', ', @commands; -} -sub intersect { - my ($a, $b) = @_; - my %union; - foreach my $e (@{$a}) { $union{$e} = 1 } + $address =~ s/.*@(.*)/$1/; + my $c = NDIRC::Context->new({ + host => $address, + nick => $nick, + channel => $channel, + disp => $disp, + model => $model, + server => $server, + reply_string => $reply_string, + }); - my %isect; - foreach my $e (@{$b}) { - $isect{$e} = 1 if ( exists $union{$e} ) - } - return keys %isect; + return $disp->run_command($c,$command,$args); } 1; diff --git a/ndawn.pl b/ndawn.pl new file mode 100644 index 0000000..6ddc8f7 --- /dev/null +++ b/ndawn.pl @@ -0,0 +1,89 @@ +#************************************************************************** +# Copyright (C) 2008 by Michael Andreen * +# * +# This program is free software; you can redistribute it and/or modify * +# it under the terms of the GNU General Public License as published by * +# the Free Software Foundation; either version 2 of the License, or * +# (at your option) any later version. * +# * +# This program is distributed in the hope that it will be useful, * +# but WITHOUT ANY WARRANTY; without even the implied warranty of * +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# GNU General Public License for more details. * +# * +# You should have received a copy of the GNU General Public License * +# along with this program; if not, write to the * +# Free Software Foundation, Inc., * +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * +#**************************************************************************/ + +use strict; +use warnings; +use vars qw($VERSION %IRSSI); + +use Irssi; +use Irssi::Irc; + +$VERSION = "0.1"; +%IRSSI = ( + authors => "harv", + contact => "harv\@ruin.nu", + name => "NewDawn testbot script", + description => "Testbot used for implementing commands for nd bots", + license => "GPL-2 or (at your option) any later version", + url => "", + changed => "", +); + +$ND::refresh = 0; + +$SIG{USR1} = sub{ + print "USR1"; + $ND::refresh = 1; +}; + + +use again 'ND::DB'; +use again 'ND::Include'; +use again 'NDIRC::Dispatcher'; +use again 'NDIRC::Context'; +use again 'NDIRC::Command'; +use again 'NDIRC::Misc'; + +my $DBH = DB(); +my $TICK = $DBH->selectrow_array('SELECT tick()'); + +my $disp = new NDIRC::Dispatcher; + +$disp->load('Basic'); + +$disp->add_channel('#testarlite', ['pub','help']); +$disp->add_channel('#testarmer', ['pub','help']); + +sub event_pubmsg { + my ($server, $msg, $nick, $address, $channel) = @_; + + if (parseCommand($msg,$server,$nick,$address,$channel,$disp,DB())){ + #Command parsed and run successfully + } +} + +sub event_privmsg { + my ($server, $msg, $nick, $address) = @_; + + if (parseCommand($msg,$server,$nick,$address,'pm',$disp,DB())){ + #Command parsed and run successfully + } +} + +sub refresh { + print "SiG" if $ND::refresh; + $ND::refresh = 0; +} + +Irssi::timeout_add(60*1000, 'refresh', undef); +Irssi::timeout_add(1000, sub{refresh if $ND::refresh}, undef); +Irssi::signal_add('message public','event_pubmsg'); +Irssi::signal_add('message private','event_privmsg'); + + -- 2.39.2