#!/usr/bin/python -tt # # SILAN # Simple IRC Log Analyzer # http://slackforge.com/silan/ # # Version 0.0.2 # An extremely simple IRC log analyzer --; # Only supports dircproxy logs, because that's what I use # # Copyright (c) 2007 Shidayu # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. ## ## # Import Other Modules # import os, re, stat, sys, glob, datetime, string, time, random, math, cgi from ConfigParser import SafeConfigParser ## # Globals # # Regular Expressions used by SILAN re_rawlog = re.compile('^\[(\d\d) ([A-Z][a-z][a-z]) (\d\d):(\d\d)\] ([^\n\r\f\v]*)$') re_ownnick = re.compile('--- You changed your nickname to (\S*)') re_nickchange = re.compile('--- (\S*) \([^\t\n\r\f\v)]*\) changed nickname to (\S*)') re_quit = re.compile('<-- (\S*) \([^\t\n\r\f\v)]*\) quit from IRC: ([^\n\r\f\v]*)$') re_join = re.compile('--> (\S*) \([^\t\n\r\f\v)]*\) joined the channel$') re_part = re.compile('<-- (\S*) \([^\t\n\r\f\v)]*\) left the channel$') re_message = re.compile('<([^ \t\n\r\f\v!]*)![^\t\n\r\f\v>]*> ([^\n\r\f\v]*)$') re_describe = re.compile('\* (\S*) ([^\n\r\f\v]*)') re_topic = re.compile('--- (\S*) \([^\t\n\r\f\v)]*\) changed topic: ([^\t\n\r\f\v]*)$') re_kick = re.compile('<-- (\S*) kicked off by (\S*) \([^\t\n\r\f\v)]*\): ([^\n\r\f\v]*)$') re_ownkick = re.compile('<-- Kicked off by (\S*) \([^\t\n\r\f\v)]*\): ([^\n\r\f\v]*)$') re_nonword = re.compile('[^a-zA-Z0-9]') re_channame = re.compile('^(\S*).log$') re_tpltag = re.compile('{{silan:([^\t\n\r\f\v}]*)}}') # Month conversion Dictionary fmt_month = { "Jan": 1, "Feb": 2, "Mar": 3, "Apr": 4, "May": 5, "Jun": 6, "Jul": 7, "Aug": 8, "Sep": 9, "Oct": 10, "Nov": 11, "Dec": 12 } ## # Class Definitions # class Silan(object): """ Simple IRC Log Analyzer """ def __init__(self): self.config = SafeConfigParser() self.config.read("/Users/shidayu/Applications/silan-0.0.2/silan.ini") self.networks = [] for network in self.config.get("global", "networks").split(): print "Added network:", network self.networks.append(Network(self.config, network)) for network in self.networks: for channel in network.channels: outfile = open(self.config.get(network.name + "." + channel.name, "output"), "w") outfile.write(Report(channel).output_string()) outfile.close() class UserEvent(object): """ Line of log data represented only with a a user and a time """ def __init__(self, dttm, user): self.dttm = dttm self.user = user class Join(UserEvent): def __init__(self, dttm, user): UserEvent.__init__(self, dttm, user) class Part(UserEvent): def __init__(self, dttm, user): UserEvent.__init__(self, dttm, user) class Line(object): """ Line of log data with date and time """ def __init__(self, dttm, text): self.dttm = dttm self.text = text def word_count(self): return len(self.text.split()) class UserLine(Line): """ Line of log data that includes user instance """ def __init__(self, dttm, user, text): Line.__init__(self, dttm, text) self.user = user class Quit(Line): def __init__(self, dttm, text): Line.__init__(self, dttm, text) class Message(UserLine): """ Standard IRC message from a user """ def __init__(self, dttm, user, text): UserLine.__init__(self, dttm, user, text) class Describe(UserLine): """ Describe or action from a user """ def __init__(self, dttm, user, text): UserLine.__init__(self, dttm, user, text) class Topic(UserLine): """ Channel topic """ def __init__(self, dttm, user, text): UserLine.__init__(self, dttm, user, text) class Kick(UserLine): """ Channel kick """ def __init__(self, dttm, target, user, text): UserLine.__init__(self, dttm, user, text) self.target = target class Progress(object): def __init__(self, min, max, ratchet): self.min = min self.max = max self.ratchet = ratchet self.progress = min self.crank = -1 def tock(self, amount): self.progress += amount if int(math.floor(float(self.progress)/float(self.max) * 100)) > self.crank: self.crank = int(math.floor(float(self.progress)/float(self.max) * 100)) if self.crank % self.ratchet == 0: print str(self.crank) + "%", sys.stdout.flush() class Log(object): """ Log recorded by IRC client/proxy """ def __init__(self, filename): self.filename = filename self.lines = [] self._read_log() def _read_log(self): print "Reading lines from", self.filename + "...", sys.stdout.flush() loghandler = open(self.filename, 'r') loglist = loghandler.readlines() loghandler.close() print "done" print "Parsing timestamps and text from", self.filename + ":", sys.stdout.flush() creationdttm = datetime.datetime.fromtimestamp(os.stat(self.filename).st_ctime) firstday = int(re_rawlog.match(loglist[0]).group(1)) firstmonth = fmt_month[re_rawlog.match(loglist[0]).group(2)] firsthour = int(re_rawlog.match(loglist[0]).group(3)) firstminute = int(re_rawlog.match(loglist[0]).group(4)) prevdttm = datetime.datetime(creationdttm.year, firstmonth, firstday, firsthour, firstminute) meter = Progress(0, len(loglist), 5) for logtext in loglist: day = int(re_rawlog.match(logtext).group(1)) month = fmt_month[re_rawlog.match(logtext).group(2)] hour = int(re_rawlog.match(logtext).group(3)) minute = int(re_rawlog.match(logtext).group(4)) text = re_rawlog.match(logtext).group(5) if month < prevdttm.month or (month == prevdttm.month and day < prevdttm.day): # The preceding: Does it look like we've gone back in time? prevdttm = datetime.datetime(prevdttm.year + 1, month, day, hour, minute) else: prevdttm = datetime.datetime(prevdttm.year, month, day, hour, minute) self.lines.append(Line(prevdttm, text)) meter.tock(1) class Network(Log): """ IRC network """ def __init__(self, config, name="default"): self.name = name self.channels = [] self.users = [] self.ownuser = None self.config = config self.no_protips = [] Log.__init__(self, self.config.get(self.name + ".preferences", "file")) print "Processing manual nick configuration for", self.name + "...", sys.stdout.flush() for nick in self.config.options(self.name + ".alternates"): for alt in self.config.get(self.name + ".alternates", nick).split(): self.add_alternate(nick, alt) for nick in self.config.options(self.name + ".separates"): for sep in self.config.get(self.name + ".separates", nick).split(): self.add_separate(nick, sep) for nick in self.config.options(self.name + ".propercase"): self.get_user(nick).name = self.config.get(self.name + ".propercase", nick) for nick in self.config.get(self.name + ".preferences", "no_protips").split(): self.no_protips.append(self.get_user(nick)) print "done" self._parse() for channel in self.config.get(self.name + ".preferences", "channels").split(): print "Added channel to network", self.name + ":", channel self.channels.append(Channel(channel, self, self.config.get(self.name + "." + channel, "file"))) def get_channel(self, channame): for netchannel in self.channels: if channame.lower() == netchannel.name.lower(): return netchannel return None def get_user(self, nickname): netuser = self.find_user(nickname) if not netuser: newuser = User(nickname) self.users.append(newuser) return newuser else: return netuser def find_user(self, nickname): for netuser in self.users: if nick_belongs_to_user(nickname, netuser): return netuser def add_separate(self, nick1, nick2): netuser1 = self.get_user(nick1) netuser2 = self.get_user(nick2) if netuser1 == netuser2: if netuser1.name.lower() == nick1.lower(): netuser1.alternates.remove(nick2) newuser = self.get_user(nick2) newuser.separates.append(nick1) netuser1.separates.append(nick2) if netuser2.name.lower() == nick2.lower(): netuser2.alternates.remove(nick1) newuser = self.get_user(nick1) newuser.separates.append(nick2) netuser2.separates.append(nick1) else: netuser1.separates.append(nick2) netuser2.separates.append(nick1) def add_alternate(self, nick1, nick2): netuser1 = self.find_user(nick1) netuser2 = self.find_user(nick2) if not netuser1 and not netuser2: newuser = self.get_user(nick1) newuser.alternates.append(nick2) elif netuser1 and not netuser2: if nick2.lower() not in [sep.lower() for sep in netuser1.separates]: netuser1.alternates.append(nick2) else: self.get_user(nick2) elif netuser2 and not netuser1: if nick1.lower() not in [sep.lower() for sep in netuser2.separates]: netuser2.alternates.append(nick1) else: self.get_user(nick1) elif netuser1 and netuser2 and netuser1 != netuser2 and not are_separate(netuser1, netuser2): if self.users.index(netuser1) < self.users.index(netuser2): netuser1.alternates.append(netuser2.name) netuser1.alternates.extend(netuser2.alternates) self.users.remove(netuser2) if self.ownuser == netuser2: self.ownuser = netuser1 for netchan in self.channels: netchan.reassign_events(netuser2, netuser1) del netuser2 else: netuser2.alternates.append(netuser1.name) netuser2.alternates.extend(netuser1.alternates) self.users.remove(netuser1) if self.ownuser == netuser1: self.ownuser = netuser2 for netchan in self.channels: netchan.reassign_events(netuser1, netuser2) del netuser1 def _parse(self): meter = Progress(0, len(self.lines), 5) print "Parsing events from log file for network", self.name + ":", for line in self.lines: if re_nickchange.match(line.text): oldnick = re_nickchange.match(line.text).group(1) newnick = re_nickchange.match(line.text).group(2) self.add_alternate(oldnick, newnick) if re_ownnick.match(line.text): newnick = re_ownnick.match(line.text).group(1) if not self.ownuser: self.ownuser = self.get_user(newnick) else: self.add_alternate(self.ownuser.name, newnick) if re_quit.match(line.text): quitnick = re_quit.match(line.text).group(1) quittext = re_quit.match(line.text).group(2) self.get_user(quitnick).quits.append(Quit(line.dttm, cgi.escape(quittext))) meter.tock(1) class Channel(Log): """ IRC channel """ def __init__(self, name, network, filename): Log.__init__(self, filename) self.name = name self.network = network self.topics = [] self.messages = [] self.describes = [] self.kicks = [] self.joins = [] self.parts = [] self._parse() def reassign_events(self, userfrom, userto): for message in self.get_messages_by_user(userfrom): message.user = userto for describe in self.get_describes_by_user(userfrom): describe.user = userto for kick in self.get_kicks_by_user(userfrom): kick.user = userto for kick in self.get_kicks_by_target(userfrom): kick.target = userto for topic in self.get_topics_by_user(userfrom): topic.user = userto def get_line_count_by_hour(self, hour): return len([line for line in self.lines if line.dttm.hour == hour]) def get_line_count_by_hour_range(self, starthour, endhour): return len([line for line in self.lines if line.dttm.hour >= starthour and line.dttm.hour <= endline]) def get_line_count_by_weekday(self, weekday): return len([line for line in self.lines if line.dttm.weekday() == weekday]) def get_line_count_by_user(self, user): return len(self.get_messages_by_user(user)) + len(self.get_describes_by_user(user)) + len(self.get_kicks_by_user(user)) def get_messages_by_hour(self, hour): return [msg for msg in self.messages if msg.dttm.hour == hour] def get_messages_by_hour_range(self, starthour, endhour): return [msg for msg in self.messages if msg.dttm.hour >= starthour and msg.dttm.hour <= endhour] def get_messages_by_weekday(self, weekday): return [msg for msg in self.messages if msg.dttm.weekday() == weekday] def get_messages_by_user(self, user): return [msg for msg in self.messages if msg.user == user] def get_describes_by_user(self, user): return [dsc for dsc in self.describes if dsc.user == user] def get_kicks_by_user(self, user): return [kck for kck in self.kicks if kck.user == user] def get_kicks_by_target(self, user): return [kck for kck in self.kicks if kck.target == user] def get_topics_by_user(self, user): return [tpc for tpc in self.topics if tpc.user == user] def get_messages_that_match(self, regex): return [msg for msg in self.messages if regex.match(msg.text)] def get_users(self): users = [] for message in self.messages: if message.user not in users: users.append(message.user) for describe in self.describes: if describe.user not in users: users.append(describe.user) for kick in self.kicks: if kick.user not in users: users.append(kick.user) if kick.target not in users: users.append(kick.target) for topic in self.topics: if message.user not in users: users.append(topic.user) return users def _parse(self): meter = Progress(0, len(self.lines), 5) print "Parsing events from log file for channel", self.name, "on network", self.network.name + ":", for line in self.lines: if re_message.match(line.text): user = self.network.get_user(re_message.match(line.text).group(1)) text = re_message.match(line.text).group(2) self.messages.append(Message(line.dttm, user, cgi.escape(text))) elif re_describe.match(line.text): user = self.network.get_user(re_describe.match(line.text).group(1)) text = re_describe.match(line.text).group(2) self.describes.append(Describe(line.dttm, user, cgi.escape(text))) elif re_kick.match(line.text): target = self.network.get_user(re_kick.match(line.text).group(1)) user = self.network.get_user(re_kick.match(line.text).group(2)) text = re_kick.match(line.text).group(3) self.kicks.append(Kick(line.dttm, target, user, cgi.escape(text))) elif re_ownkick.match(line.text): user = self.network.get_user(re_ownkick.match(line.text).group(1)) text = re_ownkick.match(line.text).group(2) self.kicks.append(Kick(line.dttm, self.network.ownuser, user, cgi.escape(text))) elif re_topic.match(line.text): user = self.network.get_user(re_topic.match(line.text).group(1)) text = re_topic.match(line.text).group(2) self.topics.append(Topic(line.dttm, user, cgi.escape(text))) elif re_join.match(line.text): user = self.network.get_user(re_join.match(line.text).group(1)) self.joins.append(Join(line.dttm, user)) elif re_part.match(line.text): user = self.network.get_user(re_part.match(line.text).group(1)) self.parts.append(Part(line.dttm, user)) meter.tock(1) class ReportTemplate(object): def __init__(self, filename, theme="default"): self.filename = filename self.theme = theme self.contents = None self.registry = {} self._read() def _read(self): tplhandler = open("/Users/shidayu/Applications/silan-0.0.2/themes/" + self.theme + "/" + self.filename) self.contents = tplhandler.read() tplhandler.close() def output_string(self): dough = self.contents for key in self.registry: dough = dough.replace("{{silan:" + key + "}}", self.registry[key]) return dough def register_tag(self, tag, something): self.registry[tag] = something def kill_registry(self): self.registry = {} class Report(ReportTemplate): def __init__(self, channel, theme="default"): self.channel = channel ReportTemplate.__init__(self, "main.tpl", theme) self._register() def _register(self): self.register_tag("channel", cgi.escape(self.channel.name)) self.register_tag("ownusername", cgi.escape(self.channel.network.ownuser.name)) self.register_tag("daycount", cgi.escape(str((self.channel.lines[-1].dttm - self.channel.lines[0].dttm).days))) self.register_tag("startdate", cgi.escape(self.channel.lines[0].dttm.strftime("%A %B %d, %Y"))) self.register_tag("enddate", cgi.escape(self.channel.lines[-1].dttm.strftime("%A %B %d, %Y"))) self.register_tag("usercount", cgi.escape(str(len(self.channel.get_users())))) self.register_tag("messagecount", cgi.escape(str(len(self.channel.messages)))) self.register_tag("describecount", cgi.escape(str(len(self.channel.describes)))) self.register_tag("kickcount", cgi.escape(str(len(self.channel.kicks)))) self.register_tag("modules", self._modules()) def _modules(self): return ActivityByWeekday(self.channel, self.theme).output_string() + ActivityByHour(self.channel, self.theme).output_string() + UserActivity(self.channel, self.theme).output_string() + Protips(self.channel, self.theme).output_string() + TheStorySoFar(self.channel, self.theme).output_string() class ReportModule(ReportTemplate): def __init__(self, name, theme): self.name = name self.theme = theme ReportTemplate.__init__(self, "module.tpl", theme) self._register() def _register(self): self.register_tag("title", self.name) class User(object): """ IRC user """ def __init__(self, name): self.name = name self.alternates = [] self.separates = [] self.quits = [] class TheStorySoFar(ReportModule): def __init__(self, channel, theme): self.channel = channel self.theme = theme ReportModule.__init__(self, "The Story So Far", self.theme) self.re_endofsentence = re.compile(".*[?.!\"]+ ?$") self.re_targetted = re.compile("(\S*) ?[:,-;!.]+ ?(.+)") self.register_tag("contents", self._contents()) def _contents(self): print "Reporting The Story So Far for", self.channel.name, "on network", self.channel.network.name + ":", sys.stdout.flush() jacket = ReportTemplate("story.tpl", self.theme) paraformat = ReportTemplate("story.paragraph.tpl", self.theme) internal = "" textpool = [] print "gathering", sys.stdout.flush() alldescribes = [cgi.escape(dsc.user.name) + " " + dsc.text for dsc in self.channel.describes] random.shuffle(alldescribes) for describe in alldescribes: if not self.re_endofsentence.match(describe): describe += "." textpool.append(describe) for topic in [tpc.text for tpc in self.channel.topics]: if not self.re_endofsentence.match(topic): topic += "." textpool.append(topic) lastmessager = None for message in self.channel.messages: worktext = message.text if self.re_targetted.match(worktext): if self.channel.network.find_user(self.re_targetted.match(worktext).group(1)): worktext = self.re_targetted.match(worktext).group(2) if lastmessager == None: lastmessager = message.user textpool.append(worktext) elif message.user == lastmessager: if not self.re_endofsentence.match(textpool[-1]): textpool[-1] += ", " + worktext else: textpool.append(worktext) else: if not self.re_endofsentence.match(textpool[-1]): textpool[-1] += "." lastmessager = message.user textpool.append(worktext) for kick in [kck.text for kck in self.channel.kicks]: if not self.re_endofsentence.match(kick): kick += "." textpool.append(kick) para = "" w1 = "\n" w2 = "\n" # GENERATE TABLE table = {} print "chaining", readmeter = Progress(0, len(textpool), 10) for line in textpool: for word in line.split(): table.setdefault( (w1, w2), [] ).append(word) w1, w2 = w2, word readmeter.tock(1) table.setdefault( (w1, w2), [] ).append("\n") # Mark the end of the file # GENERATE OUTPUT w1 = "\n" w2 = "\n" maxwords = 512 minsentperpara = 4 maxsentperpara = 12 cursent = 0 for i in xrange(maxwords): newword = random.choice(table[(w1, w2)]) if newword == "\n": break para += newword + " " w1, w2 = w2, newword if self.re_endofsentence.match(para): cursent += 1 if cursent >= minsentperpara and (cursent >= maxsentperpara or random.randint(minsentperpara, maxsentperpara) == cursent): paraformat.register_tag("paragraph", para) internal += paraformat.output_string() para = "" cursent = 0 if para != "": paraformat.register_tag("paragraph", para) internal += paraformat.output_string() jacket.register_tag("paragraphs", internal) return jacket.output_string() class Protips(ReportModule): def __init__(self, channel, theme): self.channel = channel self.theme = theme self.re_protip = re.compile("^protip.*[:.,>-]+.+") ReportModule.__init__(self, "Protips", self.theme) self.register_tag("contents", self._contents()) def _contents(self): print "Reporting Protips for", self.channel.name, "on network", self.channel.network.name + "...", sys.stdout.flush() jacket = ReportTemplate("generalmatches.tpl", self.theme) tipformat = ReportTemplate("generalmatches.match.tpl", self.theme) internal = "" targetmessages = self.channel.get_messages_that_match(self.re_protip) for protip in targetmessages: if protip.user not in self.channel.network.no_protips: tipformat.register_tag("dttm", cgi.escape(protip.dttm.strftime("%Y %b %d %I:%M %p"))) tipformat.register_tag("name", cgi.escape(protip.user.name)) tipformat.register_tag("quote", protip.text) internal += tipformat.output_string() tipformat.kill_registry() jacket.register_tag("matches", internal) print "done" return jacket.output_string() class UserActivity(ReportModule): def __init__(self, channel, theme): self.channel = channel self.theme = theme ReportModule.__init__(self, "User Activity", self.theme) self.register_tag("contents", self._contents()) def _contents(self): print "Reporting User Activity for", self.channel.name, "on network", self.channel.network.name + ":", sys.stdout.flush() jacket = ReportTemplate("useractivity.tpl", self.theme) userformat = ReportTemplate("useractivity.user.tpl", self.theme) internal = "" print "counting", sys.stdout.flush() bymsgct = [(self.channel.get_line_count_by_user(user), user) for user in self.channel.get_users()] print "sorting", sys.stdout.flush() bymsgct.sort() bymsgct.reverse() print "outputting", sys.stdout.flush() meter = Progress(0, min(25, len(bymsgct)), 10) for index in xrange(0, min(25, len(bymsgct))): userandct = bymsgct[index] quoteminwords = 4 userformat.register_tag("rank", str(bymsgct.index(userandct) + 1)) userformat.register_tag("name", cgi.escape(userandct[1].name)) userformat.register_tag("count", str(userandct[0])) if userandct[0] > 0: usermessages = self.channel.get_messages_by_user(userandct[1]) quotestring = None while quotestring == None and quoteminwords > 0: usablemessages = [msg for msg in usermessages if msg.word_count() >= quoteminwords] if len(usablemessages) > 0: quotestring = usablemessages[random.randint(0, len(usablemessages) - 1)].text else: quoteminwords -= 1 userformat.register_tag("quote", quotestring) else: userformat.register_tag("quote", "") internal += userformat.output_string() userformat.kill_registry() meter.tock(1) jacket.register_tag("users", internal) return jacket.output_string() class ActivityByHour(ReportModule): def __init__(self, channel, theme): self.channel = channel self.theme = theme ReportModule.__init__(self, "Activity by Hour", self.theme) self.register_tag("contents", self._contents()) def _contents(self): print "Reporting Activity by Hour for", self.channel.name, "on network", self.channel.network.name + "...", sys.stdout.flush() jacket = ReportTemplate("activitybyhour.tpl", self.theme) hourformat = ReportTemplate("activitybyhour.hour.tpl", self.theme) internal = "" hourbox = [] for hour in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]: hourbox.append((self.channel.get_line_count_by_hour(hour), hour)) hourmax = max([inhour[0] for inhour in hourbox]) for envelope in hourbox: if hourmax > 0: hourformat.register_tag("gapsize", str(150 - (int(float(envelope[0]) / float(hourmax) * 150)))) hourformat.register_tag("barsize", str(int(float(envelope[0]) / float(hourmax) * 150))) else: hourformat.register_tag("gapsize", "150") hourformat.register_tag("barsize", "0") hourformat.register_tag("count", str(envelope[0])) hourformat.register_tag("hour", str(envelope[1])) internal += hourformat.output_string() hourformat.kill_registry() jacket.register_tag("hours", internal) print "done" return jacket.output_string() class ActivityByWeekday(ReportModule): def __init__(self, channel, theme): self.channel = channel self.theme = theme ReportModule.__init__(self, "Activity by Day of the Week", self.theme) self.register_tag("contents", self._contents()) def _contents(self): print "Reporting Activity by Day of the Week for", self.channel.name, "on network", self.channel.network.name + "...", sys.stdout.flush() jacket = ReportTemplate("activitybyweekday.tpl", self.theme) dayformat = ReportTemplate("activitybyweekday.day.tpl", self.theme) internal = "" daybox = [] for day in [6, 0, 1, 2, 3, 4, 5]: daybox.append((self.channel.get_line_count_by_weekday(day), day)) daymax = max([inday[0] for inday in daybox]) for envelope in daybox: if daymax > 0: dayformat.register_tag("gapsize", str(150 - (int(float(envelope[0]) / float(daymax) * 150)))) dayformat.register_tag("barsize", str(int(float(envelope[0]) / float(daymax) * 150))) else: dayformat.register_tag("gapsize", "150") dayformat.register_tag("barsize", "0") dayformat.register_tag("count", str(envelope[0])) dayformat.register_tag("day", {6: "Sun", 0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat"}[envelope[1]]) internal += dayformat.output_string() dayformat.kill_registry() jacket.register_tag("days", internal) print "done" return jacket.output_string() ## # Functions # def are_separate(user1, user2): for sep in user1.separates: if nick_belongs_to_user(sep, user2): return 1 for sep in user2.separates: if nick_belongs_to_user(sep, user1): return 1 return 0 def nick_belongs_to_user(nick, user): return nick.lower() == user.name.lower() or nick.lower() in [alt.lower() for alt in user.alternates] def main(): appversion = "0.0.2" print '\n\nSILAN', appversion, '- Simple IRC Log Analyzer - http://slackforge.com/silan/' print 'Copyright (c) 2007 Shidayu ' try: appinstance = Silan() except: print "An unexpected error has occurred:", sys.exc_info()[0] raise ## # Begin Here! # if __name__=="__main__": main()