Class IrcLogModule
In: lib/rbot/core/irclog.rb
Parent: CoreBotModule
IrcLogModule CoreBotModule dot/f_6.png
Author:Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>

Methods

Attributes

nolog_rx  [RW] 

Public Class methods

[Source]

    # 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

Public Instance methods

[Source]

    # 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

[Source]

    # File lib/rbot/core/irclog.rb, line 77
77:   def cleanup
78:     @queue << nil
79:     @thread.join
80:     @thread = nil
81:   end

[Source]

    # 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

[Source]

    # File lib/rbot/core/irclog.rb, line 73
73:   def irclog(message, where="server")
74:     @queue.push [message, where]
75:   end

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

    # 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

[Source]

     # 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

[Source]

     # File lib/rbot/core/irclog.rb, line 190
190:   def modechange(m)
191:     irclog "@ Mode #{m.logmessage} by #{m.source}", m.target
192:   end

[Source]

     # 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

[Source]

     # 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

[Source]

    # 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

[Source]

     # File lib/rbot/core/irclog.rb, line 244
244:   def unknown_message(m)
245:     irclog m.logmessage, ".unknown"
246:   end

[Source]

     # File lib/rbot/core/irclog.rb, line 114
114:   def welcome(m)
115:     irclog "joined server #{m.server} as #{m.target}", "server"
116:   end

Protected Instance methods

[Source]

     # 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

[Validate]