]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/core/irclog.rb
irclog: log own CTCP message with correct syntax
[user/henk/code/ruby/rbot.git] / lib / rbot / core / irclog.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: rbot IRC logging facilities
5 #
6 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7
8 class IrcLogModule < CoreBotModule
9
10   Config.register Config::IntegerValue.new('irclog.max_open_files',
11     :default => 20, :validate => Proc.new { |v| v > 0 },
12     :desc => "Maximum number of irclog files to keep open at any one time.")
13   Config.register Config::ArrayValue.new('irclog.no_log',
14     :default => [], :on_change => Proc.new { |bot, v|
15       bot.plugins.delegate 'event_irclog_list_changed', v, bot.config['irclog.do_log']
16     },
17     :desc => "List of channels and nicks for which logging is disabled. IRC patterns can be used too.")
18   Config.register Config::ArrayValue.new('irclog.do_log',
19     :default => [], :on_change => Proc.new { |bot, v|
20       bot.plugins.delegate 'event_irclog_list_changed', bot.config['irclog.no_log'], v
21     },
22     :desc => "List of channels and nicks for which logging is enabled. IRC patterns can be used too. This can be used to override wide patters in irclog.no_log")
23
24   attr :nolog_rx, :dolog_rx
25   def initialize
26     super
27     @logs = Hash.new
28     Dir.mkdir("#{@bot.botclass}/logs") unless File.exist?("#{@bot.botclass}/logs")
29     event_irclog_list_changed(@bot.config['irclog.no_log'], @bot.config['irclog.do_log'])
30   end
31
32   def can_log_on(where)
33     return true if @dolog_rx and where.match @dolog_rx
34     return false if @nolog_rx and where.match @nolog_rx
35     return true
36   end
37
38   def event_irclog_list_changed(nolist, dolist)
39     @nolog_rx = nolist.empty? ? nil : Regexp.union(*(nolist.map { |r| r.to_irc_regexp }))
40     debug "no log: #{@nolog_rx}"
41     @dolog_rx = dolist.empty? ? nil : Regexp.union(*(dolist.map { |r| r.to_irc_regexp }))
42     debug "do log: #{@dolog_rx}"
43     @logs.inject([]) { |ar, kv|
44       ar << kv.first unless can_log_on(kv.first)
45       ar
46     }.each { |w| logfile_close(w, 'logging disabled here') }
47   end
48
49   def logfile_close(where_str, reason = 'unknown reason')
50     f = @logs.delete(where_str) or return
51     stamp = Time.now.strftime '%Y/%m/%d %H:%M:%S'
52     f[1].puts "[#{stamp}] @ Log closed by #{@bot.myself.nick} (#{reason})"
53     f[1].close
54   end
55
56   # log IRC-related message +message+ to a file determined by +where+.
57   # +where+ can be a channel name, or a nick for private message logging
58   def irclog(message, where="server")
59     message = message.chomp
60     now = Time.now
61     stamp = now.strftime("%Y/%m/%d %H:%M:%S")
62     if where.class <= Server
63       where_str = "server"
64     else
65       where_str = where.downcase.gsub(/[:!?$*()\/\\<>|"']/, "_")
66     end
67     return unless can_log_on(where_str)
68     unless @logs.has_key? where_str
69       if @logs.size > @bot.config['irclog.max_open_files']
70         @logs.keys.sort do |a, b|
71           @logs[a][0] <=> @logs[b][0]
72         end.slice(0, @logs.size - @bot.config['irclog.max_open_files']).each do |w|
73           logfile_close w, "idle since #{@logs[w][0]}"
74         end
75       end
76       f = File.new("#{@bot.botclass}/logs/#{where_str}", "a")
77       f.sync = true
78       f.puts "[#{stamp}] @ Log started by #{@bot.myself.nick}"
79       @logs[where_str] = [now, f]
80     end
81     @logs[where_str][1].puts "[#{stamp}] #{message}"
82     @logs[where_str][0] = now
83     #debug "[#{stamp}] <#{where}> #{message}"
84   end
85
86   def cleanup
87     @logs.keys.each { |w| logfile_close(w, 'rescan or shutdown') }
88   end
89
90   def sent(m)
91     case m
92     when NoticeMessage
93       irclog "-#{m.source}- #{m.message}", m.target
94     when PrivMessage
95       logtarget = who = m.target
96       if m.ctcp
97         case m.ctcp.intern
98         when :ACTION
99           irclog "* #{m.source} #{m.logmessage}", logtarget
100         when :VERSION
101           irclog "@ #{m.source} asked #{who} about version info", logtarget
102         when :SOURCE
103           irclog "@ #{m.source} asked #{who} about source info", logtarget
104         when :PING
105           irclog "@ #{m.source} pinged #{who}", logtarget
106         when :TIME
107           irclog "@ #{m.source} asked #{who} what time it is", logtarget
108         else
109           irclog "@ #{m.source} asked #{who} about #{[m.ctcp, m.message].join(' ')}", logtarget
110         end
111       else
112         irclog "<#{m.source}> #{m.logmessage}", logtarget
113       end
114     when QuitMessage
115       m.was_on.each { |ch|
116         irclog "@ quit (#{m.message})", ch
117       }
118     end
119   end
120
121   def welcome(m)
122     irclog "joined server #{m.server} as #{m.target}", "server"
123   end
124
125   def listen(m)
126     case m
127     when PrivMessage
128       method = 'log_message'
129     else
130       method = 'log_' + m.class.name.downcase.match(/^irc::(\w+)message$/).captures.first
131     end
132     if self.respond_to?(method)
133       self.__send__(method, m)
134     else
135       warning "unhandled logging for #{m.pretty_inspect} (no such method #{method})"
136       unknown_message(m)
137     end
138   end
139
140   def log_message(m)
141     if m.ctcp
142       who = m.private? ? "me" : m.target
143       logtarget = m.private? ? m.source : m.target
144       case m.ctcp.intern
145       when :ACTION
146         if m.public?
147           irclog "* #{m.source} #{m.logmessage}", m.target
148         else
149           irclog "* #{m.source}(#{m.sourceaddress}) #{m.logmessage}", m.source
150         end
151       when :VERSION
152         irclog "@ #{m.source} asked #{who} about version info", logtarget
153       when :SOURCE
154         irclog "@ #{m.source} asked #{who} about source info", logtarget
155       when :PING
156         irclog "@ #{m.source} pinged #{who}", logtarget
157       when :TIME
158         irclog "@ #{m.source} asked #{who} what time it is", logtarget
159       else
160         irclog "@ #{m.source} asked #{who} about #{[m.ctcp, m.message].join(' ')}", logtarget
161       end
162     else
163       if m.public? 
164         irclog "<#{m.source}> #{m.logmessage}", m.target
165       else
166         irclog "<#{m.source}(#{m.sourceaddress})> #{m.logmessage}", m.source
167       end
168     end
169   end
170
171   def log_notice(m)
172     if m.private?
173       irclog "-#{m.source}(#{m.sourceaddress})- #{m.logmessage}", m.source
174     else
175       irclog "-#{m.source}- #{m.logmessage}", m.target
176     end
177   end
178
179   def motd(m)
180     m.message.each_line { |line|
181       irclog "MOTD: #{line}", "server"
182     }
183   end
184
185   def log_nick(m)
186     m.is_on.each { |ch|
187       irclog "@ #{m.oldnick} is now known as #{m.newnick}", ch
188     }
189   end
190
191   def log_quit(m)
192     m.was_on.each { |ch|
193       irclog "@ Quit: #{m.source}: #{m.logmessage}", ch
194     }
195   end
196
197   def modechange(m)
198     irclog "@ Mode #{m.logmessage} by #{m.source}", m.target
199   end
200
201   def log_join(m)
202     if m.address?
203       debug "joined channel #{m.channel}"
204       irclog "@ Joined channel #{m.channel}", m.channel
205     else
206       irclog "@ #{m.source} joined channel #{m.channel}", m.channel
207     end
208   end
209
210   def log_part(m)
211     if(m.address?)
212       debug "left channel #{m.channel}"
213       irclog "@ Left channel #{m.channel} (#{m.logmessage})", m.channel
214     else
215       irclog "@ #{m.source} left channel #{m.channel} (#{m.logmessage})", m.channel
216     end
217   end
218
219   def log_kick(m)
220     if(m.address?)
221       debug "kicked from channel #{m.channel}"
222       irclog "@ You have been kicked from #{m.channel} by #{m.source} (#{m.logmessage})", m.channel
223     else
224       irclog "@ #{m.target} has been kicked from #{m.channel} by #{m.source} (#{m.logmessage})", m.channel
225     end
226   end
227
228   # def log_invite(m)
229   #   # TODO
230   # end
231
232   def log_topic(m)
233     case m.info_or_set
234     when :set
235       if m.source == @bot.myself
236         irclog "@ I set topic \"#{m.topic}\"", m.channel
237       else
238         irclog "@ #{m.source} set topic \"#{m.topic}\"", m.channel
239       end
240     when :info
241       topic = m.channel.topic
242       irclog "@ Topic is \"#{m.topic}\"", m.channel
243       irclog "@ Topic set by #{topic.set_by} on #{topic.set_on}", m.channel
244     end
245   end
246
247   # def names(m)
248   #   # TODO
249   # end
250
251   def unknown_message(m)
252     irclog m.logmessage, ".unknown"
253   end
254 end
255
256 ilm = IrcLogModule.new
257 ilm.priority = -1
258