Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@activitycentral.org>2011-04-26 11:28:25 (GMT)
committer Aleksey Lim <alsroot@activitycentral.org>2011-04-26 11:28:25 (GMT)
commit00be5916f9fd46aad6cb0b20c0187ffc8b3e1d5d (patch)
tree981d5e48b3076dd01b96a9d5c4ffe49d10b70e4d
Extract from puppet module
-rw-r--r--__init__.py60
-rw-r--r--config.py50
-rw-r--r--plugin.py211
3 files changed, 321 insertions, 0 deletions
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: