#!/usr/bin/env python # # ircCMMLBot.py # Copyright (C) 2004 CSIRO Australia # # Original idea by Ben Leslie; # # cmmlbot: hahaha you are my spawn! # benno: I am a CMML log bot # # Hacked up by Conrad Parker, April 2004. First tested during the 2004 # Linux Audio Conference, which had simultaneous Ogg Vorbis streaming and # IRC chat. # # Based on the example ircLogBot.py from: # http://twistedmatrix.com/documents/current/examples/ircLogBot.py # # Twisted, the Framework of Your Internet # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """An IRC log bot - logs a channel's events to a CMML file. If someone says the bot's name in the channel followed by a ':', e.g. cmmlbot: hello! the bot will reply: foo: I am a CMML log bot Run this script with two arguments, the channel name the bot should connect to, and file to log to, e.g.: $ python ircCMMLBot.py test test.cmml will log channel #test to the file 'test.cmml'. NOTE: Nothing more than the stream tag will get written until cmmlbot receives the topic info. If the channel contains no topic, nothing more than the tag is written. """ # twisted imports from twisted.protocols import irc from twisted.internet import reactor, protocol from twisted.python import log # system imports import time, sys from xml.sax.saxutils import escape def time2npt (t): it = int(t) ms = (t*1000) - (float(it) * 1000) ss = it % 60 mm = ((it-ss)/60) % 60 hh = ((it-(mm*60)-ss)/3600) % 60 return "npt:%d:%02d:%02d.%03d" % (hh, mm, ss, ms) class MessageLogger: """ An independant logger class (because separation of application and protocol logic is a good thing). """ def __init__(self, file): self.file = file self.start_time = time.time() timestamp = self.gettimestamp() self.file.write('\n') self.file.write('\n' % timestamp) self.got_head = False def gettimestamp(self): #timestamp = time.strftime("clock:%Y%m%dT%H%M%SZ", time.localtime(time.time())) timestamp = time2npt(time.time() - self.start_time) return timestamp def head(self, topic): self.file.write('\n') self.file.write('%s\n' % topic) self.file.write('\n') self.got_head = True # Totally dodgy URL grabber, probably ok enough for grabbing urls from # irc ... Amazing technique as used by Plone, so it must be good ;-) # This version has been improved by including gopher:// def grabURL(self, message): for w in message.split(): for scheme in ['http://', 'https://', 'ftp://', 'gopher://']: if (w.startswith (scheme)): return w return None def log(self, message): """Write a message to the file.""" if (not self.got_head): return timestamp = self.gettimestamp() message = escape(message) self.file.write('%s\n' % (timestamp, message)) self.file.flush() def user_log(self, user, message): """Write a user message to the file.""" if (not self.got_head): return timestamp = self.gettimestamp() url = self.grabURL(message) message = escape(message) self.file.write('\n' % timestamp) self.file.write('\n' % user) if (url is not None): self.file.write('%s\n' % (url, url)) self.file.write('%s\n' % message) self.file.write('\n') self.file.flush() def close(self): self.file.write('\n') self.file.close() class LogBot(irc.IRCClient): """A logging IRC bot.""" def __init__(self): self.nickname = "cmmlbot" def connectionMade(self): irc.IRCClient.connectionMade(self) self.logger = MessageLogger(open(self.factory.filename, "a")) #self.logger.log("[connected at %s]" % # time.asctime(time.localtime(time.time()))) def connectionLost(self, reason): irc.IRCClient.connectionLost(self, reason) #self.logger.log("[disconnected at %s]" % # time.asctime(time.localtime(time.time()))) self.logger.close() # callbacks for events def signedOn(self): """Called when bot has succesfully signed on to server.""" self.join(self.factory.channel) def joined(self, channel): """This will get called when the bot joins the channel.""" #self.logger.log("[I have joined %s]" % channel) def topicUpdated(self, user, channel, newTopic): if (not self.logger.got_head): self.logger.head(newTopic) else: self.logger.log("Topic changed to: %s" % newTopic) def privmsg(self, user, channel, msg): """This will get called when the bot receives a message.""" user = user.split('!', 1)[0] #self.logger.log("%s: %s" % (user, msg)) self.logger.user_log(user, msg) if msg.startswith("%s:" % self.nickname): # someone is talking to me, lets respond: msg = "%s: I am a CMML log bot" % user self.say(channel, msg) self.logger.user_log(self.nickname, msg) def action(self, user, channel, msg): """This will get called when the bot sees someone do an action.""" user = user.split('!', 1)[0] self.logger.log("* %s %s" % (user, msg)) # irc callbacks def irc_NICK(self, prefix, params): """Called when an IRC user changes their nickname.""" old_nick = prefix.split('!')[0] new_nick = params[0] self.logger.log("%s is now known as %s" % (old_nick, new_nick)) class LogBotFactory(protocol.ClientFactory): """A factory for LogBots. A new protocol instance will be created each time we connect to the server. """ # the class of the protocol to build when new connection is made protocol = LogBot def __init__(self, channel, filename): self.channel = channel self.filename = filename def clientConnectionLost(self, connector, reason): """If we get disconnected, reconnect to server.""" connector.connect() def clientConnectionFailed(self, connector, reason): print "connection failed:", reason reactor.stop() if __name__ == '__main__': # initialize logging log.startLogging(sys.stdout) # create factory protocol and application try: f = LogBotFactory(sys.argv[1], sys.argv[2]) except IndexError: print 'Usage: cmmlbot channel filename' sys.exit(1) # connect factory to this host and port reactor.connectTCP("irc.freenode.net", 6667, f) # run bot reactor.run()