root/lang/perl/tiarra/trunk/module/Log/Logger.pm @ 8977

Revision 8977, 6.3 kB (checked in by hio, 5 years ago)

lang/perl/tiarra: TALK時の整形をLog::Channelから拝借.

  • Property svn:mime-type set to text/x-perl; charset=EUC-JP
  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id URL Date Rev Author
Line 
1# -----------------------------------------------------------------------------
2# $Id$
3# -----------------------------------------------------------------------------
4package Log::Logger;
5use strict;
6use warnings;
7use Multicast;
8
9our $MARKER = {
10  myself => {
11    PRIVMSG => ['>','<'],
12    NOTICE  => [')','('],
13  },
14  priv => {
15    PRIVMSG => ['-','-'],
16    NOTICE  => ['=','='],
17  },
18  channel => {
19    PRIVMSG => ['<','>'],
20    NOTICE  => ['(',')'],
21  },
22};
23
24sub new {
25    my ($class,$enstringed_callback,$exception_object,@exceptions) = @_;
26    # enstringed_callback:
27    #   メッセージをログ文字列化した時に呼ばれる関数。CODE型。
28    #   引数を二つ取り、一つ目はチャンネル名、二つ目はログ文字列。
29    # exception_object:
30    #   exceptionsで指定されたメソッドを呼ぶとき、どのオブジェクトで呼ぶか。
31    # exceptions:
32    #   特定のメッセージのログ文字列化をオーバーライドする
33    #   'S_PRIVMSG'等。
34    #   引数は(Tiarra::IRC::Message,IrcIO)、戻り値は[チャンネル名,ログ文字列]の配列
35    my $this = {
36        enstringed => $enstringed_callback,
37        exception_object => $exception_object,
38        exceptions => do {
39            my %hash = map { $_ => 1 } @exceptions;
40            \%hash;
41        },
42    };
43    bless $this,$class;
44}
45
46sub log {
47    my ($this,$msg,$sender) = @_;
48    my $prefix = do {
49        if ($sender->isa('IrcIO::Server')) {
50            'S';
51        }
52        elsif ($sender->isa('IrcIO::Client')) {
53            'C';
54        }
55    };
56    my $method_name = "${prefix}_".$msg->command;
57    my @results;
58    # このメソッドはexceptionsで定義されているか?
59    if (defined $this->{exceptions}->{$method_name}) {
60        eval {
61            @results = $this->{exception_object}->$method_name($msg,$sender);
62        }; if ($@) {
63            RunLoop->shared->notify_error($@);
64        }
65    }
66    else {
67        # このクラスにメソッドはあるか?
68        if ($this->can($method_name)) {
69            eval {
70                @results = $this->$method_name($msg,$sender);
71            }; if ($@) {
72                RunLoop->shared->notify_error($@);
73            }
74        }
75    }
76   
77    foreach (@results) {
78        $this->{enstringed}->($_->[0],$_->[1]);
79    }
80}
81
82sub S_JOIN {
83    my ($this,$msg,$sender) = @_;
84   
85    $msg->param(0) =~ m/^([^\x07]+)(?:\x07(.*))?/;
86    my ($ch_name,$mode) = ($1,(defined $2 ? $2 : ''));
87    $mode =~ tr/ov/@+/;
88
89    [$msg->param(0),
90     sprintf('+ %s%s (%s) to %s',
91             $mode,$msg->nick,$msg->prefix,$msg->param(0))];
92}
93
94sub S_PART {
95    my ($this,$msg,$sender) = @_;
96    if (defined $msg->param(1)) {
97        [$msg->param(0),
98         sprintf('- %s from %s (%s)',
99                 $msg->nick,$msg->param(0),$msg->param(1))];
100    } else {
101        [$msg->param(0),
102         sprintf('- %s from %s',
103                 $msg->nick,$msg->param(0))];
104    }
105}
106
107sub S_KICK {
108    my ($this,$msg,$sender) = @_;
109    # RFC2812には、「サーバはクライアントに複数のチャンネルやユーザのKICKメッセージを
110    # 送っては「いけません」。これは、古いクライアントソフトウェアとの下位互換のためです。」とある。
111    [$msg->param(0),
112     sprintf('- %s by %s from %s (%s)',
113             $msg->param(1),$msg->nick,$msg->param(0),$msg->param(2))];
114}
115
116sub S_INVITE {
117    my ($this,$msg,$sender) = @_;
118    [$msg->param(1),
119        sprintf 'Invited by %s: %s',$msg->nick,$msg->param(1)];
120}
121
122sub S_MODE {
123    my ($this,$msg,$sender) = @_;
124    [$msg->param(0),
125     sprintf('Mode by %s: %s %s',
126             $msg->nick,
127             $msg->param(0),
128             join(' ',@{$msg->params}[1 .. ($msg->n_params - 1)]))];
129}
130
131sub S_NICK {
132    my ($this,$msg,$sender) = @_;
133    my $network_name = $sender->network_name;
134    my $line = do {
135        sprintf(
136            do {
137                if ($msg->param(0) eq $sender->current_nick) {
138                    'My nick is changed (%s -> %s)';
139                }
140                else {
141                    '%s -> %s';
142                }
143            },
144            $msg->nick,
145            $msg->param(0));
146    };
147    my @result;
148    if( my $ch_short_list = $msg->remark('affected-channels') ){
149    foreach my $ch_name (@$ch_short_list) {
150        push @result,[Multicast::attach($ch_name,$network_name),
151                      $line];
152    }
153    }
154    @result;
155}
156
157{
158no warnings 'once';
159*S_KILL = \&S_QUIT;
160}
161
162sub S_QUIT {
163    my ($this,$msg,$sender) = @_;
164    my $network_name = $sender->network_name;
165    my @result;
166    if( my $ch_short_list = $msg->remark('affected-channels') ){
167    foreach my $ch_name (@$ch_short_list) {
168        push @result,[Multicast::attach($ch_name,$network_name),
169                      sprintf '! %s (%s)',$msg->nick,$msg->param(0)];
170    }
171    }
172    @result;
173}
174
175sub S_TOPIC {
176    my ($this,$msg,$sender) = @_;
177    [$msg->param(0),
178     sprintf('Topic of channel %s by %s: %s',
179             $msg->param(0),
180             $msg->nick,
181             $msg->param(1))];
182}
183
184{
185no warnings 'once';
186*S_PRIVMSG = \&PRIVMSG_or_NOTICE;
187*S_NOTICE  = \&PRIVMSG_or_NOTICE;
188*C_PRIVMSG = \&PRIVMSG_or_NOTICE;
189*C_NOTICE  = \&PRIVMSG_or_NOTICE;
190}
191
192sub PRIVMSG_or_NOTICE
193{
194  my ($this,$msg,$sender) = @_;
195  my $line = $this->_build_message($msg, $sender);
196  my $channel = $line->{is_priv} ? 'priv' : $line->{ch_long};
197  [$channel, $line->{formatted}];
198}
199
200# -----------------------------------------------------------------------------
201# $hashref = $obj->_build_message($msg, $sender).
202# Log/Channel から拝借.
203# ただ
204# - distinguish_myself が省かれている.
205# - PRIVでも相手の名前がchannel名として使われる.
206# - 好きにformat出来るように解析した情報をHASHREFで返している.
207# という点で変更されている.
208#
209sub _build_message
210{
211  my ($this, $msg, $sender) = @_;
212
213  my $raw_target = $msg->param(0);
214  my ($target,$netname,$_explicit) = Multicast::detatch( $raw_target );
215  my $is_priv = Multicast::nick_p($target);
216  my $cmd     = $msg->command;
217
218  my $marker_id;
219  if( $sender->isa('IrcIO::Client') )
220  {
221    $marker_id = 'myself';
222  }elsif( $is_priv )
223  {
224    $marker_id = 'priv';
225  }else
226  {
227    $marker_id = 'channel';
228  }
229  my $marker = $MARKER->{$marker_id}{$cmd};
230  $marker or die "no marker for $marker_id/$cmd";
231
232  my ($speaker, $ch_short);
233  if( $sender->isa('IrcIO::Client') )
234  {
235    # 自分の発言.
236    $speaker  = RunLoop->shared_loop->network( $netname )->current_nick;
237    $ch_short = $target;
238  }else
239  {
240    # 相手の.
241    $speaker  = $msg->nick || $sender->current_nick;
242    $ch_short = $is_priv ? $speaker : $target;
243  }
244  my $ch_long = Multicast::attach($ch_short, $netname);
245
246  my $line = sprintf(
247    '%s%s:%s%s %s',
248    $marker->[0],
249    $ch_long,
250    $speaker,
251    $marker->[1],
252    $msg->param(1),
253  );
254
255  +{
256    marker_id => $marker_id, # 'myself' / 'priv' / 'channel'
257    is_priv   => $is_priv,
258    marker    => $marker,    # ['<', '>'], etc.
259    speaker   => $speaker,
260    ch_long   => $ch_long,
261    ch_short  => $ch_short,
262    netname   => $netname,
263    msg       => $msg->param(1),
264    formatted => $line,
265  };
266}
267
2681;
Note: See TracBrowser for help on using the browser.