1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
# -----------------------------------------------------------------------------
# $Id: Grant.pm 15318 2008-07-06 15:34:51Z hio $
# -----------------------------------------------------------------------------
package Channel::Mode::Oper::Grant;
use strict;
use warnings;
use base qw(Module);
use Mask;
use Multicast;
use Timer;
use base qw(Tiarra::Mixin::NewIRCMessage);
sub new {
my $class = shift;
my $this = $class->SUPER::new(@_);
$this->{queue} = {}; # network name => [[channel(short),nick], ...]
$this->{timer} = undef; # queueが空でない時だけ必要になるTimer
$this;
}
sub destruct {
my ($this) = @_;
if (defined $this->{timer}) {
$this->{timer}->uninstall;
}
}
sub message_arrived {
my ($this,$msg,$sender) = @_;
# 先に進むための条件:
# 1. サーバーからのメッセージである
# 2. コマンドはJOINである
# 3. 自分のJOINではない
# 4. @付きのJOINではない
# 5. そのチャンネルで自分は@を持っている
# 6. 相手はmaskに一致する
if ($sender->isa('IrcIO::Server') &&
$msg->command eq 'JOIN' &&
defined $msg->nick &&
$msg->nick ne RunLoop->shared->current_nick) {
foreach (split /,/,$msg->param(0)) {
my ($ch_full,$mode) = (m/^(.+?)(?:\x07(.*))?$/);
my $ch_short = Multicast::detatch($ch_full);
my $ch = $sender->channel($ch_short);
my $myself = $ch->names($sender->current_nick);
if (defined $myself && $myself->has_o && (!defined $mode || $mode !~ /o/)) {
if (Mask::match_deep_chan([$this->config->mask('all')],$msg->prefix,$ch_full)) {
# waitで指定された秒数の経過後に、キューに入れる。
# 同時にキュー消化タイマーを準備する。
$this->push_to_queue($sender,$ch_short,$msg->nick);
}
}
}
}
$msg;
}
sub push_to_queue {
my ($this,$server,$ch_short,$nick) = @_;
my $wait = $this->config->wait || 0;
if ($wait =~ /^\s*(\d+)\s*-\s*(\d+)\s*$/) {
$wait = int(rand($2 - $1 + 1)) + $1;
}
Timer->new(
After => $wait,
Code => sub {
# 対象の人が既に+oされていたら中止。
my $ch = $server->channel($ch_short);
return if !defined $ch;
my $target = $ch->names($nick);
return if !defined $target;
return if $target->has_o;
my $queue = $this->{queue}->{$server->network_name};
if (!defined $queue) {
$queue = $this->{queue}->{$server->network_name} = [];
}
push @$queue,[$ch_short,$nick];
$this->prepare_timer;
})->install;
}
sub prepare_timer {
my ($this) = @_;
# キュー消化タイマーが存在しなければ作る
if (!defined $this->{timer}) {
$this->{timer} = Timer->new(
Interval => 0, # 勿論、最初のtriggerで変更する。
Repeat => 1,
Code => sub {
my ($timer) = @_;
$timer->interval(1);
# 鯖毎に3つずつ消化する。
# チャンネル毎に最大3つずつ纏める。
foreach my $network_name (keys %{$this->{queue}}) {
my $queue = $this->{queue}->{$network_name};
my $server = $this->_runloop->network($network_name);
my $channels = {}; # ch_shortname => [nick,nick,...]
for (my $i = 0; @$queue && $i < 3; $i++) {
my $elem = shift(@$queue);
my $nicks = $channels->{$elem->[0]};
if (!defined $nicks) {
$nicks = $channels->{$elem->[0]} = [];
}
push @$nicks,$elem->[1];
}
while (my ($ch_short,$nicks) = each %$channels) {
$server->send_message(
$this->construct_irc_message(
Command => 'MODE',
Params => [$ch_short,
'+'.('o' x @$nicks),
@$nicks]));
}
# キューが空になったらキーごと消す。
delete $this->{queue}->{$network_name} unless @$queue;
}
# 全てのキューが空になったら終了。
if (!%{$this->{queue}}) {
$timer->uninstall;
$this->{timer} = undef;
}
})->install;
}
}
1;
=pod
info: 特定のチャンネルに特定の人間がjoinした時に、自分がチャンネルオペレータ権限を持っていれば+oする。
default: off
section: important
# splitからの復帰などで+o対象の人が一度に大量に入って来ても+oは少しずつ実行します。
# Excess Floodにはならない筈ですが、本格的な防衛BOTに使える程の物ではありません。
# 対象の人間がjoinしてから実際に+oするまで何秒待つか。
# 省略されたら待ちません。
# 5-10 のように指定されると、その値の中でランダムに待ちます。
wait: 2-5
# チャンネルと人間のマスクを定義。Auto::Operと同様。
-mask: * example!~example@*.example.ne.jp
=cut
|