| Class | IrcLogModule |
| In: |
lib/rbot/core/irclog.rb
|
| Parent: | CoreBotModule |
| Author: | Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com> |
| nolog_rx | [RW] |
# File lib/rbot/core/irclog.rb, line 31
31: def initialize
32: super
33: @queue = Queue.new
34: @thread = Thread.new { loggers_thread }
35: @logs = Hash.new
36: logdir = @bot.path 'logs'
37: Dir.mkdir(logdir) unless File.exist?(logdir)
38: # TODO what shall we do if the logdir couldn't be created? (e.g. it existed as a file)
39: event_irclog_list_changed(@bot.config['irclog.no_log'], @bot.config['irclog.do_log'])
40: @fn_format = @bot.config['irclog.filename_format']
41: end
# File lib/rbot/core/irclog.rb, line 43
43: def can_log_on(where)
44: return true if @dolog_rx and where.match @dolog_rx
45: return false if @nolog_rx and where.match @nolog_rx
46: return true
47: end
# File lib/rbot/core/irclog.rb, line 77
77: def cleanup
78: @queue << nil
79: @thread.join
80: @thread = nil
81: end
# File lib/rbot/core/irclog.rb, line 53
53: def event_irclog_list_changed(nolist, dolist)
54: @nolog_rx = nolist.empty? ? nil : Regexp.union(*(nolist.map { |r| r.to_irc_regexp }))
55: debug "no log: #{@nolog_rx}"
56: @dolog_rx = dolist.empty? ? nil : Regexp.union(*(dolist.map { |r| r.to_irc_regexp }))
57: debug "do log: #{@dolog_rx}"
58: @logs.inject([]) { |ar, kv|
59: ar << kv.first unless can_log_on(kv.first)
60: ar
61: }.each { |w| logfile_close(w, 'logging disabled here') }
62: end
log IRC-related message message to a file determined by where. where can be a channel name, or a nick for private message logging
# File lib/rbot/core/irclog.rb, line 73
73: def irclog(message, where="server")
74: @queue.push [message, where]
75: end
# File lib/rbot/core/irclog.rb, line 118
118: def listen(m)
119: case m
120: when PrivMessage
121: method = 'log_message'
122: else
123: method = 'log_' + m.class.name.downcase.match(/^irc::(\w+)message$/).captures.first
124: end
125: if self.respond_to?(method)
126: self.__send__(method, m)
127: else
128: warning "unhandled logging for #{m.pretty_inspect} (no such method #{method})"
129: unknown_message(m)
130: end
131: end
# File lib/rbot/core/irclog.rb, line 194
194: def log_join(m)
195: if m.address?
196: debug "joined channel #{m.channel}"
197: irclog "@ Joined channel #{m.channel}", m.channel
198: else
199: irclog "@ #{m.source} joined channel #{m.channel}", m.channel
200: end
201: end
# File lib/rbot/core/irclog.rb, line 212
212: def log_kick(m)
213: if(m.address?)
214: debug "kicked from channel #{m.channel}"
215: irclog "@ You have been kicked from #{m.channel} by #{m.source} (#{m.logmessage})", m.channel
216: else
217: irclog "@ #{m.target} has been kicked from #{m.channel} by #{m.source} (#{m.logmessage})", m.channel
218: end
219: end
# File lib/rbot/core/irclog.rb, line 133
133: def log_message(m)
134: if m.ctcp
135: who = m.private? ? "me" : m.target
136: logtarget = m.private? ? m.source : m.target
137: case m.ctcp.intern
138: when :ACTION
139: if m.public?
140: irclog "* #{m.source} #{m.logmessage}", m.target
141: else
142: irclog "* #{m.source}(#{m.sourceaddress}) #{m.logmessage}", m.source
143: end
144: when :VERSION
145: irclog "@ #{m.source} asked #{who} about version info", logtarget
146: when :SOURCE
147: irclog "@ #{m.source} asked #{who} about source info", logtarget
148: when :PING
149: irclog "@ #{m.source} pinged #{who}", logtarget
150: when :TIME
151: irclog "@ #{m.source} asked #{who} what time it is", logtarget
152: else
153: irclog "@ #{m.source} asked #{who} about #{[m.ctcp, m.message].join(' ')}", logtarget
154: end
155: else
156: if m.public?
157: irclog "<#{m.source}> #{m.logmessage}", m.target
158: else
159: irclog "<#{m.source}(#{m.sourceaddress})> #{m.logmessage}", m.source
160: end
161: end
162: end
# File lib/rbot/core/irclog.rb, line 178
178: def log_nick(m)
179: (m.is_on & @bot.myself.channels).each { |ch|
180: irclog "@ #{m.oldnick} is now known as #{m.newnick}", ch
181: }
182: end
# File lib/rbot/core/irclog.rb, line 164
164: def log_notice(m)
165: if m.private?
166: irclog "-#{m.source}(#{m.sourceaddress})- #{m.logmessage}", m.source
167: else
168: irclog "-#{m.source}- #{m.logmessage}", m.target
169: end
170: end
# File lib/rbot/core/irclog.rb, line 203
203: def log_part(m)
204: if(m.address?)
205: debug "left channel #{m.channel}"
206: irclog "@ Left channel #{m.channel} (#{m.logmessage})", m.channel
207: else
208: irclog "@ #{m.source} left channel #{m.channel} (#{m.logmessage})", m.channel
209: end
210: end
# File lib/rbot/core/irclog.rb, line 184
184: def log_quit(m)
185: (m.was_on & @bot.myself.channels).each { |ch|
186: irclog "@ Quit: #{m.source}: #{m.logmessage}", ch
187: }
188: end
def log_invite(m)
# TODO
end
# File lib/rbot/core/irclog.rb, line 225
225: def log_topic(m)
226: case m.info_or_set
227: when :set
228: if m.source == @bot.myself
229: irclog "@ I set topic \"#{m.topic}\"", m.channel
230: else
231: irclog "@ #{m.source} set topic \"#{m.topic}\"", m.channel
232: end
233: when :info
234: topic = m.channel.topic
235: irclog "@ Topic is \"#{m.topic}\"", m.channel
236: irclog "@ Topic set by #{topic.set_by} on #{topic.set_on}", m.channel
237: end
238: end
# File lib/rbot/core/irclog.rb, line 64
64: def logfile_close(where_str, reason = 'unknown reason')
65: f = @logs.delete(where_str) or return
66: stamp = timestamp(Time.now)
67: f[1].puts "#{stamp} @ Log closed by #{@bot.myself.nick} (#{reason})"
68: f[1].close
69: end
# File lib/rbot/core/irclog.rb, line 248
248: def logfilepath(where_str, now)
249: @bot.path('logs', now.strftime(@fn_format) % { :where => where_str })
250: end
# File lib/rbot/core/irclog.rb, line 190
190: def modechange(m)
191: irclog "@ Mode #{m.logmessage} by #{m.source}", m.target
192: end
# File lib/rbot/core/irclog.rb, line 172
172: def motd(m)
173: m.message.each_line { |line|
174: irclog "MOTD: #{line}", "server"
175: }
176: end
# File lib/rbot/core/irclog.rb, line 83
83: def sent(m)
84: case m
85: when NoticeMessage
86: irclog "-#{m.source}- #{m.message}", m.target
87: when PrivMessage
88: logtarget = who = m.target
89: if m.ctcp
90: case m.ctcp.intern
91: when :ACTION
92: irclog "* #{m.source} #{m.logmessage}", logtarget
93: when :VERSION
94: irclog "@ #{m.source} asked #{who} about version info", logtarget
95: when :SOURCE
96: irclog "@ #{m.source} asked #{who} about source info", logtarget
97: when :PING
98: irclog "@ #{m.source} pinged #{who}", logtarget
99: when :TIME
100: irclog "@ #{m.source} asked #{who} what time it is", logtarget
101: else
102: irclog "@ #{m.source} asked #{who} about #{[m.ctcp, m.message].join(' ')}", logtarget
103: end
104: else
105: irclog "<#{m.source}> #{m.logmessage}", logtarget
106: end
107: when QuitMessage
108: m.was_on.each { |ch|
109: irclog "@ quit (#{m.message})", ch
110: }
111: end
112: end
# File lib/rbot/core/irclog.rb, line 49
49: def timestamp(time)
50: return time.strftime(@bot.config['irclog.timestamp_format'])
51: end
def names(m)
# TODO
end
# File lib/rbot/core/irclog.rb, line 244
244: def unknown_message(m)
245: irclog m.logmessage, ".unknown"
246: end
# File lib/rbot/core/irclog.rb, line 114
114: def welcome(m)
115: irclog "joined server #{m.server} as #{m.target}", "server"
116: end
# File lib/rbot/core/irclog.rb, line 253
253: def loggers_thread
254: ls = nil
255: debug 'loggers_thread starting'
256: while ls = @queue.pop
257: message, where = ls
258: message = message.chomp
259: now = Time.now
260: stamp = timestamp(now)
261: if where.class <= Server
262: where_str = "server"
263: else
264: where_str = where.downcase.gsub(/[:!?$*()\/\\<>|"']/, "_")
265: end
266: next unless can_log_on(where_str)
267:
268: # close the previous logfile if we're rotating
269: if @logs.has_key? where_str
270: fp = logfilepath(where_str, now)
271: logfile_close(where_str, 'log rotation') if fp != @logs[where_str][1].path
272: end
273:
274: # (re)open the logfile if necessary
275: unless @logs.has_key? where_str
276: if @logs.size > @bot.config['irclog.max_open_files']
277: @logs.keys.sort do |a, b|
278: @logs[a][0] <=> @logs[b][0]
279: end.slice(0, @logs.size - @bot.config['irclog.max_open_files']).each do |w|
280: logfile_close w, "idle since #{@logs[w][0]}"
281: end
282: end
283: fp = logfilepath(where_str, now)
284: begin
285: dir = File.dirname(fp)
286: # first of all, we check we're not trying to build a directory structure
287: # where one of the components exists already as a file, so we
288: # backtrack along dir until we come across the topmost existing name.
289: # If it's a file, we rename to filename.old.filedate
290: up = dir.dup
291: until File.exist? up
292: up.replace(File.dirname(up))
293: end
294: unless File.directory? up
295: backup = up.dup
296: backup << ".old." << File.atime(up).strftime('%Y%m%d%H%M%S')
297: debug "#{up} is not a directory! renaming to #{backup}"
298: File.rename(up, backup)
299: end
300: FileUtils.mkdir_p(dir)
301: # conversely, it may happen that fp exists and is a directory, in
302: # which case we rename the directory instead
303: if File.directory? fp
304: backup = fp.dup
305: backup << ".old." << File.atime(fp).strftime('%Y%m%d%H%M%S')
306: debug "#{fp} is not a file! renaming to #{backup}"
307: File.rename(fp, backup)
308: end
309: # it should be fine to create the file now
310: f = File.new(fp, "a")
311: f.sync = true
312: f.puts "#{stamp} @ Log started by #{@bot.myself.nick}"
313: rescue Exception => e
314: error e
315: next
316: end
317: @logs[where_str] = [now, f]
318: end
319: @logs[where_str][1].puts "#{stamp} #{message}"
320: @logs[where_str][0] = now
321: #debug "#{stamp} <#{where}> #{message}"
322: end
323: @logs.keys.each { |w| logfile_close(w, 'rescan or shutdown') }
324: debug 'loggers_thread terminating'
325: end