зеркало из https://github.com/mozilla/pjs.git
337 строки
16 KiB
Plaintext
337 строки
16 KiB
Plaintext
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
|
################################
|
|
# God Module #
|
|
################################
|
|
|
|
package BotModules::God;
|
|
use vars qw(@ISA);
|
|
@ISA = qw(BotModules);
|
|
1;
|
|
|
|
sub Help {
|
|
my $self = shift;
|
|
my ($event) = @_;
|
|
my $answer = {
|
|
'' => 'A per-channel auto-opper.',
|
|
'ops' => 'Lists the autoop list for a channel. Syntax: \'ops in <channel>\'',
|
|
'opme' => 'Checks the autoop list, and ops the speaker if they are on the autoop list. Must be used in a channel. Syntax: \'op me\' or \'opme\'',
|
|
'mask' => 'Add or remove a regexp mask from a channel\'s autoop list. Only bot and channel admins can do this. USE OF THIS FEATURE IS HIGHLY DISCOURAGED AS IT IS VERY INSECURE!!! Syntax: \'add mask <user@host> in <channel>\' to add and \'remove mask <user@host> in <channel>\' to remove. The special word \'everywhere\' can be used instead of a channel name to add a mask that works in all channels.',
|
|
'autoop' => 'Add someone to the autoop list for a channel. Only bot and channel admins can do this. Syntax: \'op <user> in <channel>\'',
|
|
'deautoop' => 'Remove someone from the autoop list for a channel. Only bot and channel admins can do this. Syntax: \'deautoop <user> in <channel>\'',
|
|
'enable' => 'Enable a module in a channel. Only bot and channel admins can do this. Syntax: \'enable <module> in <channel>\'',
|
|
'disable' => 'Disable a module in a channel. Only bot and channel admins can do this. Syntax: \'disable <module> in <channel>\'',
|
|
};
|
|
if ($self->isAdmin($event)) {
|
|
$answer->{'opme'} .= '. As an administrator, you can also say \'op me in <channel>\' or \'op me everywhere\' which will do the obvious things.';
|
|
$answer->{'promote'} = 'Add someone to the channel admin list for a channel. Only bot admins can do this. Syntax: \'promote <user> in <channel>\'',
|
|
$answer->{'demote'} = 'Remove someone from the channel admin list for a channel. Only bot admins can do this. Syntax: \'demote <user> in <channel>\'',
|
|
}
|
|
return $answer;
|
|
}
|
|
|
|
# RegisterConfig - Called when initialised, should call registerVariables
|
|
sub RegisterConfig {
|
|
my $self = shift;
|
|
$self->SUPER::RegisterConfig(@_);
|
|
$self->registerVariables(
|
|
# [ name, save?, settable? ]
|
|
['channelAdmins', 1, 1, {}],
|
|
['channelOps', 1, 1, {}],
|
|
['channelOpMasks', 1, 1, {}],
|
|
['kickLog', 1, 1, []],
|
|
['allowPrivateOpRequests', 1, 1, 1],
|
|
['maxInChannel', 1, 1, 4],
|
|
);
|
|
}
|
|
|
|
sub Told {
|
|
my $self = shift;
|
|
my ($event, $message) = @_;
|
|
if ($event->{'level'} == 1) {
|
|
if ($message =~ /^\s*(?:list\s+)?ops\s+(?:in\s+|for\s+)?(\S+)\s*\??$/osi) {
|
|
my $channel = lc($1);
|
|
$self->listOps($event, $channel);
|
|
} elsif ($message =~ /^\s*autoop\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
|
if (($self->isChannelAdmin($event, $2)) or ($self->isAdmin($event))) {
|
|
my $channel = $2 eq 'everywhere' ? '' : lc($2);
|
|
$self->{'channelOps'}->{$channel} .= " $1";
|
|
$self->saveConfig();
|
|
$self->say($event, "$event->{'from'}: User '$1' added to the autoop list of channel '$2'.");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Only channel administrators may add people to a channel's autoop list.");
|
|
}
|
|
} elsif ($message =~ /^\s*deautoop\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
|
if (($self->isChannelAdmin($event, $2)) or ($self->isAdmin($event))) {
|
|
my $channel = $2 eq 'everywhere' ? '' : lc($2);
|
|
my %people = map { $_ => 1 } split(/ +/os, $self->{'channelOps'}->{$channel});
|
|
delete($people{$1}); # get rid of any mentions of that person
|
|
$self->{'channelOps'}->{$channel} = join(' ', keys(%people));
|
|
$self->saveConfig();
|
|
$self->say($event, "$event->{'from'}: User '$1' removed from the autoop list of channel '$2'.");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Only channel administrators may remove people from a channel's autoop list.");
|
|
}
|
|
} elsif ($message =~ /^\s*add\s+mask\s+(\S+)\s+(?:in|to|for|from)\s+(\S+)\s*$/osi) {
|
|
if (($self->isChannelAdmin($event, $2)) or ($self->isAdmin($event))) {
|
|
my $channel = $2 eq 'everywhere' ? '' : lc($2);
|
|
$self->{'channelOpMasks'}->{$channel} .= " $1";
|
|
$self->saveConfig();
|
|
$self->say($event, "$event->{'from'}: Mask '$1' added to the autoop list of channel '$2'.");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Only channel administrators may add masks to a channel's autoop list.");
|
|
}
|
|
} elsif ($message =~ /^\s*remove\s+mask\s+(\S+)\s+(?:in|from|for|to)\s+(\S+)\s*$/osi) {
|
|
if (($self->isChannelAdmin($event, $2)) or ($self->isAdmin($event))) {
|
|
my $channel = $2 eq 'everywhere' ? '' : lc($2);
|
|
my %people = map { $_ => 1 } split(/ +/os, $self->{'channelOpMasks'}->{$channel});
|
|
delete($people{$1}); # get rid of any mentions of that person
|
|
$self->{'channelOpMasks'}->{$channel} = join(' ', keys(%people));
|
|
$self->saveConfig();
|
|
$self->say($event, "$event->{'from'}: Mask '$1' removed from the autoop list of channel '$2'.");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Only channel administrators may remove masks from a channel's autoop list.");
|
|
}
|
|
} elsif ($message =~ /^\s*promote\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
|
if ($self->isAdmin($event)) {
|
|
$self->{'channelAdmins'}->{lc($2)} .= " $1";
|
|
$self->saveConfig();
|
|
$self->say($event, "$event->{'from'}: User '$1' promoted to channel administrator status in channel '$2'.");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Only administrators may promote people to channel admin status.");
|
|
}
|
|
} elsif ($message =~ /^\s*demote\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
|
if ($self->isAdmin($event)) {
|
|
my %people = map { $_ => 1 } split(/ +/os, $self->{'channelAdmins'}->{lc($2)});
|
|
delete($people{$1}); # get rid of any mentions of that person
|
|
$self->{'channelAdmins'}->{lc($2)} = join(' ', keys(%people));
|
|
$self->saveConfig();
|
|
$self->say($event, "$event->{'from'}: User '$1' removed from the channel administrator list of channel '$2'.");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Only administrators may remove people's channel admin status.");
|
|
}
|
|
} elsif ($message =~ /^\s*enable\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
|
if (($self->isAdmin($event)) or ($self->isChannelAdmin($event, $2))) {
|
|
my $module = $self->getModule($1);
|
|
if ($1) {
|
|
push(@{$module->{'channels'}}, lc($2));
|
|
$module->saveConfig();
|
|
$self->say($event, "$event->{'from'}: Module '$1' enabled in channel '$2'.");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: There is no module called '$1', sorry.");
|
|
}
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Only channel administrators may change a module's status.");
|
|
}
|
|
} elsif ($message =~ /^\s*disable\s+(\S+)\s+in\s+(\S+)\s*$/osi) {
|
|
if (($self->isAdmin($event)) or ($self->isChannelAdmin($event, $2))) {
|
|
my $module = $self->getModule($1);
|
|
if ($1) {
|
|
my %channels = map { $_ => 1 } @{$module->{'channels'}};
|
|
delete($channels{lc($2)}); # get rid of any mentions of that channel
|
|
@{$module->{'channels'}} = keys %channels;
|
|
$module->saveConfig();
|
|
$self->say($event, "$event->{'from'}: Module '$1' disabled in channel '$2'.");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: There is no module called '$1', sorry.");
|
|
}
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Only channel administrators may change a module's status.");
|
|
}
|
|
} elsif ($message =~ /^\s*(?:(?:(?:de)?autoop|promote|demote|enable|disable|add\s+mask|remove\s+mask)\s+(\S+)|(?:list\s+)?ops)\s*$/osi) {
|
|
$self->say($event, "$event->{'from'}: You have to give a channel, as in \'<command> <who> in <channel>\'.");
|
|
|
|
# XXX next two could be merged, maybe.
|
|
} elsif ($message =~ /^\s*op\s*meh?[!1.,\s]*(?:now\s+)?(?:please|(b+[iea]+t+c+h+))?\s*[.!1]*\s*$/osi) {
|
|
if ($event->{'userName'}) {
|
|
if ($event->{'channel'}) {
|
|
unless ($self->checkOpping($event, $event->{'channel'}, $event->{'from'}, $self->isAdmin($event))) {
|
|
if ($1) {
|
|
$self->say($event, "$event->{'from'}: No way, beetch!");
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Sorry, you are not on my auto-op list.");
|
|
}
|
|
}
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: You have to use this command in public.");
|
|
}
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: You haven't authenticated yet. See 'help auth' for details.");
|
|
}
|
|
} elsif ($message =~ /^\s*(?:please\s+)?op\s*me(?:\s+in\s+(\S+)|\s+everywhere)?[\s!1.]*\s*$/osi) {
|
|
if (($self->{'allowPrivateOpRequests'}) or ($self->isAdmin($event))) {
|
|
if ($1) {
|
|
$self->checkOpping($event, lc($1), $event->{'from'}, $self->isAdmin($event));
|
|
} else {
|
|
foreach (@{$self->{'channels'}}) {
|
|
$self->checkOpping($event, $_, $event->{'from'}, $self->isAdmin($event));
|
|
}
|
|
}
|
|
} else {
|
|
$self->say($event, "$event->{'from'}: Sorry, but no. Try \'help opme\' for details on commansyntax.");
|
|
}
|
|
} else {
|
|
my $parentResult = $self->SUPER::Told(@_);
|
|
return $parentResult < 2 ? 2 : $parentResult;
|
|
}
|
|
return 0; # we've dealt with it, no need to do anything ese.
|
|
} elsif ($event->{'level'} == 2) {
|
|
if (defined($event->{'God_channel'})) {
|
|
$event->{'God_channel_rights'} = $self->isChannelAdmin($event, $event->{'God_channel'});
|
|
}
|
|
}
|
|
return $self->SUPER::Told(@_);
|
|
}
|
|
|
|
# SpottedJoin - Called when someone joins a channel
|
|
sub SpottedJoin {
|
|
my $self = shift;
|
|
my ($event, $channel, $who) = @_;
|
|
$self->checkOpping(@_, 0);
|
|
return $self->SUPER::SpottedJoin(@_); # this should not stop anything else happening
|
|
}
|
|
|
|
# do all channels when someone authenticates
|
|
sub Authed {
|
|
my $self = shift;
|
|
my ($event, $who) = @_;
|
|
foreach (@{$self->{'channels'}}) {
|
|
$self->checkOpping($event, $_, $who, 0);
|
|
}
|
|
return $self->SUPER::Authed(@_); # this should not stop anything else happening
|
|
}
|
|
|
|
# check is someone is in the opping.
|
|
sub checkOpping {
|
|
my $self = shift;
|
|
my ($event, $channel, $who, $override) = @_;
|
|
if (($self->isAutoopped($event, $channel)) or ($self->isChannelAdmin($event, $channel)) or ($override)) {
|
|
$self->mode($event, $channel, '+o', $who);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
sub isChannelAdmin {
|
|
my $self = shift;
|
|
my ($event, $channel) = @_;
|
|
return (($event->{'userName'}) and
|
|
(defined($self->{'channelAdmins'}->{$channel})) and
|
|
($self->{'channelAdmins'}->{$channel} =~ /^(|.*\s+)$event->{'userName'}(\s+.*|)$/s));
|
|
}
|
|
|
|
sub isAutoopped {
|
|
my $self = shift;
|
|
my ($event, $channel) = @_;
|
|
return ((($event->{'userName'}) and
|
|
(defined($self->{'channelOps'}->{$channel})) and
|
|
(($self->{'channelOps'}->{$channel} =~ /^(|.*\s+)$event->{'userName'}(\s+.*|)$/s) or
|
|
($self->{'channelOps'}->{''} =~ /^(|.*\s+)$event->{'userName'}(\s+.*|)$/s))) or
|
|
($self->isMatchedByMask($event, $channel)));
|
|
}
|
|
|
|
# grrrr -- this insecure feature is here by popular demand
|
|
sub isMatchedByMask {
|
|
my $self = shift;
|
|
my ($event, $channel) = @_;
|
|
my $masks;
|
|
$masks .= $self->{'channelOpMasks'}->{$channel} if defined($self->{'channelOpMasks'}->{$channel});
|
|
$masks .= ' '.$self->{'channelOpMasks'}->{''} if defined($self->{'channelOpMasks'}->{''});
|
|
if (defined($masks)) {
|
|
my @masks = split(/ +/os, $masks);
|
|
my $user = $event->{'user'};
|
|
foreach my $regexp (@masks) {
|
|
my $pattern;
|
|
if ($regexp =~ m/ ^ # start at the start
|
|
([^!@]+) # nick part
|
|
\! # nick-username delimiter
|
|
([^!@]+) # username part
|
|
\@ # username-host delimiter
|
|
([^!@]+) # host part
|
|
$ # end at the end
|
|
/osx) {
|
|
|
|
my $nick = $1;
|
|
my $user = $2;
|
|
my $host = $3;
|
|
|
|
# This was entered as an IRC hostmask so we need to
|
|
# translate it into a regular expression.
|
|
foreach ($nick, $user, $host) {
|
|
$_ = quotemeta($_); # escape regular expression magic
|
|
s/\\\*/.*/gos; # translate "*" into regexp equivalent
|
|
}
|
|
|
|
# If we don't match the first part of the host-mask
|
|
# (the user's nick) then we should not op them; we
|
|
# should just skip to the next mask.
|
|
next unless $event->{'from'} =~ m/^$nick$/i;
|
|
|
|
# ok, create hostmask regexp
|
|
$pattern = "^$user\@$host\$";
|
|
} else {
|
|
# this was entered as a regexp, check it is valid.
|
|
$pattern = $self->sanitizeRegexp($regexp);
|
|
}
|
|
if (($pattern =~ /[^\s.*+]/os) # pattern is non-trivial
|
|
and ($user =~ /$pattern/si)) { # pattern matches user
|
|
return 1; # op user (so insecure, sigh)
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
sub Kicked {
|
|
my $self = shift;
|
|
my ($event, $channel) = @_;
|
|
push(@{$self->{'kickLog'}}, "$event->{'from'} kicked us from $channel"); # XXX karma or something... ;-)
|
|
return $self->SUPER::Kicked(@_);
|
|
}
|
|
|
|
sub getList {
|
|
my $self = shift;
|
|
my ($channel, $list) = @_;
|
|
my $data;
|
|
my @list;
|
|
$data = defined($self->{$list}->{$channel}) ? $self->{$list}->{$channel} : '';
|
|
$data .= defined($self->{$list}->{''}) ? ' '.$self->{$list}->{''} : '';
|
|
if ($data =~ /^\s*$/os) {
|
|
@list = ('(none)');
|
|
} else {
|
|
@list = sort(split(/\s+/os, $data));
|
|
while ((@list) and ($list[0] =~ /^\s*$/)) { shift @list; }
|
|
}
|
|
return @list;
|
|
}
|
|
|
|
sub listOps {
|
|
my $self = shift;
|
|
my ($event, $channel) = @_;
|
|
my @admins = $self->getList($channel, 'channelAdmins');
|
|
my @ops = $self->getList($channel, 'channelOps');
|
|
my @masks = $self->getList($channel, 'channelOpMasks');
|
|
|
|
local $" = ' ';
|
|
my @output = ();
|
|
push(@output, "$channel admins: @admins");
|
|
push(@output, "$channel ops: @ops");
|
|
if (@masks > 2) {
|
|
push(@output, "$channel autoop masks:");
|
|
foreach (@masks) {
|
|
push(@output, " $_");
|
|
}
|
|
} else {
|
|
push(@output, "$channel autoop masks: @masks");
|
|
}
|
|
if (scalar(@output) > $self->{'maxInChannel'}) {
|
|
foreach (@output) {
|
|
$self->directSay($event, $_);
|
|
}
|
|
$self->channelSay($event, "$event->{'from'}: long list /msg'ed");
|
|
} else {
|
|
foreach (@output) {
|
|
$self->say($event, "$event->{'from'}: $_");
|
|
}
|
|
}
|
|
}
|