1# ----------------------------------------------------------------------------- 2# $Id: Recent.pm 11365 2008-05-10 14:58:28Z topia $ 3# ----------------------------------------------------------------------------- 4package Log::Recent; 5use strict; 6use warnings; 7use base qw(Module); 8use Module::Use qw(Tools::DateConvert Log::Logger); 9use Tools::DateConvert; 10use Log::Logger; 11use Mask; 12 13sub new { 14 my $class = shift; 15 my $this = $class->SUPER::new(@_); 16 # チャンネル管理の手間を省くため、チャンネルのログはChannelInfoのremarksに保存する。 17 # privのログだけこのクラスで保持。 18 $this->{priv_log} = []; # 中身は単なる文字列 19 $this->{logger} = 20 Log::Logger->new( 21 sub { 22 $this->log(@_); 23 }, 24 $this, 25 'S_PRIVMSG','C_PRIVMSG','S_NOTICE','C_NOTICE'); 26 $this->{hook} = IrcIO::Client::Hook->new( 27 sub { 28 my ($hook, $client, $ch_name, $network, $ch) = @_; 29 # no-recent-logs オプションが指定されていれば何もしない 30 return if defined $client->option('no-recent-logs'); 31 # ログはあるか? 32 my $vec = $ch->remarks('recent-log'); 33 if (defined $vec) { 34 foreach my $elem (@$vec) { 35 $client->send_message( 36 $this->construct_irc_message( 37 Prefix => RunLoop->shared_loop->sysmsg_prefix(qw(channel log)), 38 Command => 'NOTICE', 39 Params => [$ch_name,$elem->[1]])); 40 } 41 } 42 })->install('channel-info'); 43 $this; 44} 45 46sub destruct { 47 my $this = shift; 48 $this->{hook} and $this->{hook}->uninstall; 49} 50 51sub message_arrived { 52 my ($this,$msg,$sender) = @_; 53 # Log::Recent/commandにマッチするか? 54 if (Mask::match(lc($this->config->command) || '*', lc($msg->command))) { 55 $this->{logger}->log($msg,$sender); 56 } 57 $msg; 58} 59 60sub client_attached { 61 my ($this,$client) = @_; 62 # no-recent-logs オプションが指定されていれば何もしない 63 return if defined $client->option('no-recent-logs'); 64 # まずはpriv 65 my $local_nick = RunLoop->shared->current_nick; 66 foreach my $elem (@{$this->{priv_log}}) { 67 $client->send_message( 68 $this->construct_irc_message( 69 Prefix => RunLoop->shared_loop->sysmsg_prefix(qw(priv log)), 70 Command => 'NOTICE', 71 Params => [$local_nick,$elem->[1]])); # $elem->[0]は常に'priv' 72 } 73 74 # 次に各チャンネル 75# foreach my $network (values %{RunLoop->shared->networks}) { 76# foreach my $ch (values %{$network->channels}) { 77# # ログはあるか? 78# my $vec = $ch->remarks('recent-log'); 79# if (defined $vec) { 80# my $ch_name; 81# foreach my $elem (@$vec) { 82# $ch_name = 83# RunLoop->shared->multi_server_mode_p ? 84# $elem->[0] : $ch->name; 85# $client->send_message( 86# $this->construct_irc_message( 87# Prefix => RunLoop->shared_loop->sysmsg_prefix(qw(channel log)), 88# Command => 'NOTICE', 89# Params => [$ch_name,$elem->[1]])); 90# } 91# } 92# } 93# } 94} 95 96*S_PRIVMSG = \&PRIVMSG_or_NOTICE; 97*S_NOTICE = \&PRIVMSG_or_NOTICE; 98*C_PRIVMSG = \&PRIVMSG_or_NOTICE; 99*C_NOTICE = \&PRIVMSG_or_NOTICE; 100sub PRIVMSG_or_NOTICE { 101 my ($this,$msg,$sender) = @_; 102 my $target = Multicast::detach($msg->param(0)); 103 my $is_priv = Multicast::nick_p($target); 104 my $cmd = $msg->command; 105 106 my $line = do { 107 if ($is_priv) { 108 if ($sender->isa('IrcIO::Client')) { 109 sprintf( 110 $cmd eq 'PRIVMSG' ? '>%s< %s' : ')%s( %s', 111 $msg->param(0), 112 $msg->param(1)); 113 } 114 else { 115 sprintf( 116 $cmd eq 'PRIVMSG' ? '-%s- %s' : '=%s= %s', 117 $msg->nick || $sender->current_nick, 118 $msg->param(1)); 119 } 120 } 121 else { 122 my $format = do { 123 if ($this->config->distinguish_myself && $sender->isa('IrcIO::Client')) { 124 $cmd eq 'PRIVMSG' ? '>%s< %s' : ')%s( %s'; 125 } 126 else { 127 $cmd eq 'PRIVMSG' ? '<%s> %s' : '(%s) %s'; 128 } 129 }; 130 my $nick = do { 131 if ($sender->isa('IrcIO::Client')) { 132 RunLoop->shared_loop->network( 133 (Multicast::detatch($msg->param(0)))[1]) 134 ->current_nick; 135 } 136 else { 137 $msg->nick || $sender->current_nick; 138 } 139 }; 140 sprintf $format,$nick,$msg->param(1); 141 } 142 }; 143 144 [$is_priv ? 'priv' : $msg->param(0),$line]; 145} 146 147sub log { 148 my ($this,$ch_full,$log_line) = @_; 149 my $vec = do { 150 if ($ch_full eq 'priv') { 151 # privは自分で保存 152 $this->{priv_log}; 153 } 154 else { 155 # privでなければChannelInfoに'recent-log'として保存。 156 my ($ch_short,$network_name) = Multicast::detach($ch_full); 157 my $network = RunLoop->shared->network($network_name); 158 if (!defined $network) { 159 RunLoop->shared->notify_warn("errorness network name: $network_name"); 160 return; 161 } 162 my $ch = $network->channel($ch_short); 163 if (!defined $ch) { 164 return; 165 } 166 my $log_vec = $ch->remarks('recent-log'); 167 if (!defined $log_vec) { 168 $log_vec = []; 169 $ch->remarks('recent-log',$log_vec); 170 } 171 $log_vec; 172 } 173 }; 174 175 my $header = Tools::DateConvert::replace( 176 $this->config->header || '%H:%M' 177 ); 178 179 # ログに追加 180 # 要素は[チャンネル名,ログ行] 181 push @$vec,[$ch_full,"$header $log_line"]; 182 183 # 溢れた分を消す 184 if (@$vec > $this->config->line) { 185 splice @$vec,0,(@$vec - $this->config->line); 186 } 187} 188 1891; 190 191=pod 192info: クライアントを接続した時に、保存しておいた最近のメッセージを送る。 193default: off 194section: important 195 196# クライアントオプションの no-recent-logs が指定されていれば送信しません。 197 198# 各行のヘッダのフォーマット。省略されたら'%H:%M'。 199header: %H:%M:%S 200 201# ログをチャンネル毎に何行まで保存するか。省略されたら10。 202line: 15 203 204# PRIVMSGとNOTICEを記録する際に、自分の発言と他人の発言でフォーマットを変えるかどうか。1/0。デフォルトで1。 205distinguish-myself: 1 206 207# どのメッセージを保存するか。省略されたら保存可能な全てのメッセージを保存する。 208command: privmsg,notice,topic,join,part,quit,kill 209=cut 210