From 00be5916f9fd46aad6cb0b20c0187ffc8b3e1d5d Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Tue, 26 Apr 2011 11:28:25 +0000 Subject: Extract from puppet module --- diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..bd57813 --- /dev/null +++ b/__init__.py @@ -0,0 +1,60 @@ +### +# Copyright (c) 2005, Jeremiah Fincher +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +""" +Logs each channel to its own individual logfile. +""" + +import supybot +import supybot.world as world + +# Use this for the version of this plugin. You may wish to put a CVS keyword +# in here if you're keeping the plugin in CVS or some similar system. +__version__ = "%%VERSION%%" + +__author__ = supybot.authors.jemfinch + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +import config +import plugin +reload(plugin) # In case we're being reloaded. +# Add more reloads here if you add third-party modules and want them to be +# reloaded when this plugin is reloaded. Don't forget to import them as well! + +if world.testing: + import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/config.py b/config.py new file mode 100644 index 0000000..e800699 --- /dev/null +++ b/config.py @@ -0,0 +1,50 @@ +### +# Copyright (c) 2005, Jeremiah Fincher +# Copyright (c) 2009, James Vega +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +import supybot.conf as conf +import supybot.registry as registry + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified himself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import expect, anything, something, yn + conf.registerPlugin('MyLogger', True) + +MyLogger = conf.registerPlugin('MyLogger') +conf.registerChannelValue(MyLogger, 'enable', + registry.Boolean(False, """Determines whether logging is enabled.""")) +conf.registerChannelValue(MyLogger, 'noLogPrefix', + registry.String('[nolog]', """Determines what string a message should be + prefixed with in order not to be logged. If you don't want any such + prefix, just set it to the empty string.""")) + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugin.py b/plugin.py new file mode 100644 index 0000000..219a9b3 --- /dev/null +++ b/plugin.py @@ -0,0 +1,211 @@ +### +# Copyright (c) 2002-2004, Jeremiah Fincher +# Copyright (c) 2009-2010, James Vega +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +import re +import time +from os.path import join, dirname, exists +from ConfigParser import ConfigParser + +import MySQLdb + +import supybot.conf as conf +import supybot.world as world +import supybot.irclib as irclib +import supybot.ircmsgs as ircmsgs +import supybot.ircutils as ircutils +import supybot.registry as registry +import supybot.callbacks as callbacks + + +class MyLogger(callbacks.Plugin): + + noIgnore = True + + def __init__(self, irc): + callbacks.Plugin.__init__(self, irc) + + self.lastMsgs = {} + self.lastStates = {} + self._conn = None + self._meetings = {} + + confile = join(dirname(world.registryFilename), 'mylogger.conf') + if not exists(confile): + self.log.warning('Config %s for MyLogger was not found', confile) + return + self._config = ConfigParser() + self._config.read(confile) + + def die(self): + if self._conn is not None: + self._conn.close() + self._conn = None + + def __call__(self, irc, msg): + try: + callbacks.Plugin.__call__(self, irc, msg) + if irc in self.lastMsgs: + if irc not in self.lastStates: + self.lastStates[irc] = irc.state.copy() + self.lastStates[irc].addMsg(irc, self.lastMsgs[irc]) + finally: + # We must make sure this always gets updated. + self.lastMsgs[irc] = msg + + def doLog(self, irc, channel, nick, message): + if nick == irc.nick and message.startswith('Meeting started'): + match = re.match('Meeting started ([^.]+) [^ ]+\.', message) + if match: + timestamp = time.strptime(match.groups()[0]) + self._meetings[channel] = time.strftime('%F %T', timestamp) + else: + self.log.warning('Cannot parse start meeting time ' \ + 'from: %s', message) + + if not self.registryValue('enable', channel) and \ + channel not in self._meetings: + return + + def db_insert(): + cursor = self._conn.cursor() + cursor.execute('INSERT INTO irclog ' \ + '(channel, day, nick, timestamp, line, meeting) ' \ + 'VALUES (%s, %s, %s, %s, %s, %s)', + (ircutils.toLower(channel), time.strftime('%F'), nick, + int(time.time()), message, + self._meetings.get(channel))) + cursor.close() + + try: + try: + db_insert() + except (AttributeError, MySQLdb.Error): + self._conn = MySQLdb.connect( + host=self._config.get('db', 'host'), + user=self._config.get('db', 'user'), + passwd=self._config.get('db', 'passwd'), + db=self._config.get('db', 'db')) + db_insert() + except MySQLdb.Error, error: + self.log.warning('Cannot store log message: %s', error) + finally: + if nick == irc.nick and message.startswith('Log:'): + if channel in self._meetings: + del self._meetings[channel] + else: + self.log.warning('Meeting start was not tracked ' \ + 'for channel %s', channel) + + def doPrivmsg(self, irc, msg): + (recipients, text) = msg.args + for channel in recipients.split(','): + if not irc.isChannel(channel): + continue + noLogPrefix = self.registryValue('noLogPrefix', channel) + if noLogPrefix and text.startswith(noLogPrefix): + continue + nick = msg.nick or irc.nick + if ircmsgs.isAction(msg): + self.doLog(irc, channel, nick, ircmsgs.unAction(msg)) + else: + self.doLog(irc, channel, nick, text) + + def doNotice(self, irc, msg): + (recipients, text) = msg.args + for channel in recipients.split(','): + if not irc.isChannel(channel): + continue + self.doLog(irc, channel, msg.nick, text) + + def doNick(self, irc, msg): + oldNick = msg.nick + newNick = msg.args[0] + for (channel, c) in irc.state.channels.iteritems(): + if newNick in c.users: + self.doLog(irc, channel, '', + '%s is now known as %s' % (oldNick, newNick)) + + def doJoin(self, irc, msg): + for channel in msg.args[0].split(','): + self.doLog(irc, channel, '', '%s <%s> has joined %s' % \ + (msg.nick, msg.prefix, channel)) + + def doKick(self, irc, msg): + if len(msg.args) == 3: + (channel, target, kickmsg) = msg.args + else: + (channel, target) = msg.args + kickmsg = '' + if kickmsg: + self.doLog(irc, channel, '', '%s was kicked by %s (%s)' % \ + (target, msg.nick, kickmsg)) + else: + self.doLog(irc, channel, '', '%s was kicked by %s' % \ + (target, msg.nick)) + + def doPart(self, irc, msg): + for channel in msg.args[0].split(','): + self.doLog(irc, channel, '', '%s has left %s' % \ + (msg.nick or msg.prefix, channel)) + + def doMode(self, irc, msg): + channel = msg.args[0] + if irc.isChannel(channel) and msg.args[1:]: + self.doLog(irc, channel, '', '%s sets mode: %s %s' % \ + (msg.nick or msg.prefix, msg.args[1], + ' '.join(msg.args[2:]))) + + def doTopic(self, irc, msg): + if len(msg.args) == 1: + return # It's an empty TOPIC just to get the current topic. + channel = msg.args[0] + self.doLog(irc, channel, '', '%s changes topic to "%s"' % \ + (msg.nick, msg.args[1])) + + def doQuit(self, irc, msg): + if not isinstance(irc, irclib.Irc): + irc = irc.getRealIrc() + for (channel, chan) in self.lastStates[irc].channels.iteritems(): + if msg.nick in chan.users: + self.doLog(irc, channel, '', '%s has quit IRC' % \ + msg.nick or msg.prefix) + + def outFilter(self, irc, msg): + # Gotta catch my own messages *somehow* :) + # Let's try this little trick... + if msg.command in ('PRIVMSG', 'NOTICE'): + # Other messages should be sent back to us. + m = ircmsgs.IrcMsg(msg=msg, prefix=irc.prefix) + self(irc, m) + return msg + + +Class = MyLogger +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: -- cgit v0.9.1