All pastes #766838 Raw Edit

silan-0.0.2 development 04

public python v1 · immutable
·published 2007-11-09 05:23 UTC
rendered paste body
#!/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 <shidayu@gmail.com>## 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, randomfrom ConfigParser import SafeConfigParser### Globals## Regular Expressions used by SILANre_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 Dictionaryfmt_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("silan.ini")				self.networks = [Network(self.config)]			channellogs = glob.glob("#*.log")		for filename in channellogs:			self.networks[0].channels.append(Channel(re_channame.match(filename).group(1), self.networks[0], filename))				for channel in self.networks[0].channels:			outname = channel.name.lower()			outname = re.sub(re_nonword, "_", outname)			outname += ".html"						outfile = open("output/" + outname, "w")			outfile.write(Report(channel).output_string())			outfile.close()								class Line(object):	""" Line of log data with date and time """		def __init__(self, dttm, text):		self.dttm = dttm		self.text = text			def wordcount(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 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 Log(object):	""" Log recorded by IRC client/proxy """		def __init__(self, filename):		self.filename = filename		self.lines = []				self._read_log()			def _read_log(self):		loghandler = open(self.filename, 'r')		loglist = loghandler.readlines()		loghandler.close()				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)				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))		class Network(Log):	""" IRC network """		def __init__(self, config, name="default", filename="Server.log"):		Log.__init__(self, filename)				self.name = name		self.channels = []		self.users = []		self.ownuser = None		self.config = config				self._config()		self._parse()		def _config(self):		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)						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):		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)		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._parse()			def reassign_events(self, userfrom, userto):		for message in get_messages_by_user(userfrom):			message.user = userto				for describe in get_describes_by_user(userfrom):			describe.user = userto				for kick in get_kicks_by_user(userfrom):			kick.user = userto				for kick in get_kicks_by_target(userfrom):			kick.target = userto					for topic in get_topics_by_user(userfrom):			topic.user = userto	def get_messages_by_hour(self, hour):		return [msg for msg in self.messages if msg.dttm.hour == hour]			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_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):		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, 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, 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, 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, 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, text))			class User(object):	""" IRC user """		def __init__(self, name):		self.name = name		self.alternates = []		self.separates = []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("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", self.channel.name)		self.register_tag("ownusername", self.channel.network.ownuser.name)		self.register_tag("daycount", str((self.channel.lines[-1].dttm - self.channel.lines[0].dttm).days))		self.register_tag("startdate", self.channel.lines[0].dttm.strftime("%A %B %d, %Y"))		self.register_tag("enddate", self.channel.lines[-1].dttm.strftime("%A %B %d, %Y"))		self.register_tag("usercount", str(len(self.channel.get_users())))		self.register_tag("modules", self._modules())		def _modules(self):		return UserStatistics(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 UserStatistics(ReportModule):	def __init__(self, channel, theme):		self.channel = channel		self.theme = theme				ReportModule.__init__(self, "User Activity Statistics", self.theme)				self.register_tag("contents", self._contents())			def _contents(self):		jacket = ReportTemplate("useractivity.tpl", self.theme)		userformat = ReportTemplate("useractivity.user.tpl", self.theme)				internal = ""				bymsgct = [(len(self.channel.get_messages_by_user(user)), user) for user in self.channel.get_users()]		bymsgct.sort()		bymsgct.reverse()				for userandct in bymsgct:			userformat.register_tag("rank", str(bymsgct.index(userandct) + 1))			userformat.register_tag("name", userandct[1].name)			userformat.register_tag("count", str(userandct[0]))			if userandct[0] > 0:				userformat.register_tag("quote", self.channel.get_messages_by_user(userandct[1])[random.randint(0, userandct[0] - 1)].text)			else:				userformat.register_tag("quote", "")									internal += userformat.output_string()						userformat.kill_registry()				jacket.register_tag("users", internal)				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 <shidayu@gmail.com>'	try:		appinstance = Silan()						except:		print "An unexpected error has occurred:", sys.exc_info()[0]		raise### Begin Here!#if __name__=="__main__":	main()