This file is indexed.

/usr/share/tiarra/module/Auto/Reply.pm is in tiarra 20100212-3.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# -*- cperl -*-
# -----------------------------------------------------------------------------
# $Id: Reply.pm 12851 2008-05-30 13:50:00Z hio $
# -----------------------------------------------------------------------------
# copyright (C) 2003 Topia <topia@clovery.jp>. all rights reserved.
package Auto::Reply;
use strict;
use warnings;
use base qw(Module);
use Module::Use qw(Auto::Utils Auto::AliasDB::CallbackUtils Tools::HashDB);
use Auto::Utils;
use Auto::AliasDB::CallbackUtils;
use Tools::HashDB;
use Mask;

our $DEFAULT_BLOCK_NAME = 'std';
our $DEFAULT_MUILTILINE_LIMIT = 10;

sub new {
    my $class = shift;
    my $this = $class->SUPER::new(@_);
    $this->{config} = [];

    eval{
      $this->_load;
    };
    if( $@ )
    {
      $this->_error("$@");
    }
    return $this;
}

sub _error
{
  my $this = shift;
  my $msg  = shift;

  $this->_runloop->notify_error(__PACKAGE__." -- ".$msg);
}

sub _load {
    my $this = shift;

    my $BLOCKS_NAME = 'blocks';
    my @block_names = $this->config->get($BLOCKS_NAME, 'all');
    if( !@block_names )
    {
      @block_names = $DEFAULT_BLOCK_NAME;
      if( !$this->config->get($DEFAULT_BLOCK_NAME) )
      {
        $this->_("Both blocks: records and std block are not defined");
        return;
      }
    }

    foreach my $blockname (@block_names) {
	if( $blockname eq $BLOCKS_NAME )
	{
	  $this->_error("block name $blockname is reserved!");
	  next;
	}
	my $block = $this->config->get($blockname);
	if( !$block )
	{
	  $this->_error("block $blockname is not defined");
	  next;
	}
	if( !UNIVERSAL::isa($block, 'Configuration::Block') )
	{
	  $this->_error("$blockname isn't block!");
	  next;
	}
	push(@{$this->{config}}, {
	    mask           => [Mask::array_or_all_chan($block->mask('all'))],
	    request        => [$block->request('all')],
	    reply_format   => [$block->reply_format('all')],
	    max_reply      => $block->max_reply,
	    rate           => $block->rate,
	    count_query    => [$block->count_query('all')],
	    count_format   => [$block->count_format('all')],
	    add            => [$block->get('add', 'all')],
	    added_format   => [$block->added_format('all')],
	    remove         => [$block->remove('all')],
	    removed_format => [$block->removed_format('all')],
	    modifier       => [$block->modifier('all')],
	    use_re         => $block->use_re,
	    multivalue     => $block->multivalue,
	    multivalue_limit => $block->multivalue_limit,
	    multivalue_seq   => 0, # updated internally.
	    database       => Tools::HashDB->new(
		$block->file,
		$block->file_encoding,
		$block->use_re,
		($block->ignore_comment ? undef : sub {0;})),
	});
    }
}

sub message_arrived {
    my ($this,$msg,$sender) = @_;
    my @result = ($msg);

    my $return_value = sub {
	return @result;
    };

    my (undef,undef,undef,$reply_anywhere,$get_full_ch_name)
	= Auto::Utils::generate_reply_closures($msg,$sender,\@result);

    if ($msg->command eq 'PRIVMSG') {
	foreach my $block (@{$this->{config}}) {
	    # count : 登録数の計算
	    if (Mask::match_deep($block->{count_query}, $msg->param(1))) {
		if (Mask::match_deep_chan($block->{mask}, $msg->prefix, $get_full_ch_name->())) {
		    # 登録数を求める
		    my $count = scalar $block->{database}->keys;
		    $reply_anywhere->($block->{count_format}, 'count' => $count);
		}
		return $return_value->();
	    }

	    my $msg_from_modifier_p = do {
		!defined $msg->prefix ||
		    Mask::match_deep_chan($block->{modifier}, $msg->prefix, $get_full_ch_name->());
	    };

	    my $tail = $msg->param(1);
	    $tail =~ s/^\s*(.*)\s*$/$1/;
	    my $keyword;
	    ($keyword, $tail) = split(/\s+/, $tail, 2);

	    if ($msg_from_modifier_p) {
		# request
		if (Mask::match_deep($block->{request}, $keyword)) {
		    # 一致する反応をリストする
		    foreach my $key (_search($block, $tail, $block->{max_reply})) {
			foreach my $message (@{$block->{database}->get_array($key)}) {
			    $reply_anywhere->($block->{reply_format},
					      'key' => $key,
					      'message' => $message);
			}
		    }
		    return $return_value->();
		}

		# add and remove
		if (defined $tail) {
		    my ($key, $param) = split(/\s+/, $tail, 2);
		    if (Mask::match_deep($block->{add}, $keyword)) {
			# 発言の追加
			# この人は変更を許可されている。
			if (defined $key && defined $param) {
			    $block->{database}->add_value($key, $param);
			    $reply_anywhere->($block->{added_format}, 'key' => $key, 'message' => $param);
			}
			return $return_value->();
		    } elsif (Mask::match_deep($block->{remove}, $keyword)) {
			# 発言の削除
			# この人は削除を許可されている。
			if (defined $key) {
			    my $count = $block->{database}->del_value($key, $param);
			    $reply_anywhere->(
				$block->{removed_format},
				'key' => $key,
				'message' => $param,
				'count' => $count);
			}
			return $return_value->();
		    }
		}
	    }

	    # match
	    if (Mask::match_deep_chan($block->{mask}, $msg->prefix, $get_full_ch_name->())) {
		my $key = (_search($block, $msg->param(1), 1, $block->{rate}))[0];
		if (defined $key) {
		  my $multivalue = $block->{multivalue} || 'random';
		  if( $multivalue eq 'all' )
		  {
		    my $limit = $block->{multivalue_limit} || $DEFAULT_MUILTILINE_LIMIT;
		    my $values = $block->{database}->get_array($key);
		    if( @$values > $limit )
		    {
		      $values = [ @$values[0..$limit-1] ];
		    }
		    $reply_anywhere->($values);
		  }elsif( $multivalue eq 'seq' || $multivalue eq 'sequence' )
		  {
		    my $values = $block->{database}->get_array($key);
		    my $seq = $block->{multivalue_seq} || 0;
		    if( $seq < 0 || $seq >= @$values )
		    {
		      $seq = 0;
		    }
		    $reply_anywhere->($values->[$seq]);
		    $block->{multivalue_seq} = ($seq + 1) % @$values;
		  }else
		  {
		    my $value = $block->{database}->get_value_random($key);
		    $reply_anywhere->($value);
		  }
		}
	    }
	}
    }

    return @result;
}

sub _search {
    # key を検索する関数。

    # $block	: 検索対象のブロック
    # $key	: 検索するキー
    # $count	: 最大発見個数。省略すると全て。
    # $rate	: 発見してもランダムに忘れる(笑)確率(パーセント)。省略すると100%。
    my ($block, $str, $count, $rate) = @_;

    my @masks;
    foreach my $mask ($block->{database}->keys) {
	if (Mask::match_array([$mask], $str, 1, $block->{use_re}, 0)) {
	    # match
	    if (!defined $rate || (int(rand() * hex('0xffffffff')) % 100) < $rate) {
		push(@masks, $mask);
		if (defined $count && $count <= scalar(@masks)) {
		    # $count 分発見したので終了。
		    last;
		}
	    }
	}
    }

    return @masks;
}

1;

=pod
info: 特定の発言に反応して発言をします。
default: off

# Auto::Aliasを有効にしていれば、エイリアス置換を行ないます。

# 使用するブロックの定義。
# 省略すると std を使用.
# 複数個の blocks の指定も可能.
blocks: std

std {
  # 1つの応答ブロックの定義.
  # 一応全ての項目が省略可能ではあるけれど,
  # 通常は最低限 file と file-encoding を使用する.
  # IRCで応答の追加削除等を行いたいときにはそれに更に設定を追加する形.
  # (IRC上で応答の追加削除は行うが保存はしない時に限ってfileを省略可能.)

  # 機能:
  # - 通常応答(mask)
  # - 登録数確認(count-query/mask)
  # - 反応確認(request/modifier)
  # - 反応追加(add/modifier)
  # - 反応削除(remove/modifier)
  # 通常応答以外は設定を省略することで機能を無効にできます。

  # データファイルと文字コードを指定します。
  # ファイルの中では一行に一つの"反応マスク:メッセージ"を書いて下さい。
  file: reply.txt
  file-encoding: euc

  # 1つの発言で複数の反応マスクにマッチする場合, 
  # どれにマッチするかは未定義です.
  # ただ, どちらか1つにのみマッチします.

  # 同じ反応マスクに複数個のメッセージが記述してあった場合の処理.
  # multivalue: random #==> ランダムに1つ選択.
  # multivalue: all    #==> 全て返す.
  # multivalue: seq    #==> 順番に1つずつ返す.
  # 省略時及び認識できなかったときは random.
  -multivalue: random
  # 返す最大行数.
  # multivalue: all の時のみ有効.
  # (それ以外の時は1行しか返さない)
  # デフォルトは 5 行まで.
  -multivalue-limit: 5

  # 反応する人のマスク。
  # 通常応答と登録数の返答時にチェックされる。
  mask: * *!*@*
  # plum: mask: *!*@*

  # マッチした1つの反応マスクが実際に発言に反応する確率を指定します。
  # 百分率です。省略された場合は100と見做されます。
  rate: 100

  # メッセージの登録数を返答するキーワードを指定します。
  # 省略するとこの機能は無効になります。
  # 指定したときだけこの機能が有効になります。
  # mask で許可された人(通常応答を返す人)が使えます。
  count-query: 反応登録数

  # メッセージの登録数を返答するときの反応を指定します。
  # formatで指定できるものと同じです。#(count)は登録数になります。
  # count-query を指定したときのみ必要。
  count-format: 反応は#(count)件登録されています。

  # メッセージを追加するキーワードを指定します。
  # ここで指定したキーワードを発言すると、新しいメッセージを追加します。
  # 実際の追加方法は「<addで指定したキーワード> <追加するメッセージ>」です。
  # 省略するとこの機能は無効になります。
  # 指定したときだけこの機能が有効になります。
  # modifier で許可された人だけ使えます。
  -add: 反応追加

  # 反応が追加されたときの反応を指定します。
  # formatで指定できるものと同じです。#(message)は追加されたメッセージになります。
  added-format: #(name|nick.now): #(key) に対する反応 #(message) を追加しました。

  # メッセージを削除するキーワードを指定します。
  # 実際の削除方法は「<removeで指定したキーワード> <削除するキーワード>」です。
  # 省略するとこの機能は無効になります。
  # 指定したときだけこの機能が有効になります。
  # modifier で許可された人だけ使えます。
  -remove: 反応削除

  # メッセージが削除されたときの反応を指定します。
  # formatで指定できるものと同じです。#(message)は削除されたメッセージになります。
  removed-format: #(name|nick.now): #(key) #(message;に対する反応 %s|;) を #(count) 件削除しました。

  # 反応の確認を行うためのキーワードを指定します。
  # 通常応答と違って, multivalue-limit の制限を受けずに全てのマッチした応答を返します。
  # 実際の指定方法は、「<requestで指定したキーワード> <チェックしたい発言>」です。
  # 省略するとこの機能は無効になります。
  # 指定したときだけこの機能が有効になります。
  # modifier で許可された人だけ使えます。
  request: 反応チェック

  # request に反応するときのフォーマットを指定します。
  # #(key) がキーワード、 #(message) が発言に置換されます。
  # request を指定したときのみ必要。
  reply-format: 「#(key)」という発言に「#(message)」と反応します。

  # request に反応する最大個数(反応マスクの数)を指定します。
  # (1つの反応マスクに対応するメッセージの数は制限されません。)
  # あまり大きな値を指定すると、アタックが可能になったり、ログが流れて邪魔なので注意してください。
  # 通常の反応には関与しません。また、応答の行数ではありません。
  max-reply: 5

  # 編集系コマンド, add とremove と request を許可する人。
  # 省略された場合は「* *!*@*」(全員許可)と見做します。
  modifier: * *!*@*

  # 正規表現拡張を許可するか。省略された場合は禁止します。
  use-re: 1
}
=cut