diff --git a/plugins/Herald/README.txt b/plugins/Herald/README.txt new file mode 100644 index 000000000..d60b47a97 --- /dev/null +++ b/plugins/Herald/README.txt @@ -0,0 +1 @@ +Insert a description of your plugin here, with any notes, etc. about using it. diff --git a/plugins/Herald/__init__.py b/plugins/Herald/__init__.py new file mode 100644 index 000000000..1bd82a630 --- /dev/null +++ b/plugins/Herald/__init__.py @@ -0,0 +1,55 @@ +### +# Copyright (c) 2004, 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. +### + +""" +Greets users who join the channel with a recognized hostmask with a nice +little greeting. +""" + +import supybot +import supybot.world as world + +__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. + +if world.testing: + import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/plugins/Herald/config.py b/plugins/Herald/config.py new file mode 100644 index 000000000..691bc3521 --- /dev/null +++ b/plugins/Herald/config.py @@ -0,0 +1,65 @@ +### +# Copyright (c) 2004, 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. +### + +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('Herald', True) + + +Herald = conf.registerPlugin('Herald') +conf.registerChannelValue(Herald, 'heralding', + registry.Boolean(True, """Determines whether messages will be sent to the + channel when a recognized user joins; basically enables or disables the + plugin.""")) +conf.registerChannelValue(Herald, 'throttleTime', + registry.PositiveInteger(600, """Determines the minimum number of seconds + between heralds.""")) +conf.registerChannelValue(Herald, 'default', + registry.String('', """Sets the default herald to use. If a user has a + personal herald specified, that will be used instead. If set to the empty + string, the default herald will be disabled.""")) +conf.registerChannelValue(Herald.default, 'notice', + registry.Boolean(True, """Determines whether the default herald will be + sent as a NOTICE instead of a PRIVMSG.""")) +conf.registerChannelValue(Herald.default, 'public', + registry.Boolean(False, """Determines whether the default herald will be + sent publicly.""")) +conf.registerChannelValue(Herald, 'throttleTimeAfterPart', + registry.PositiveInteger(60, """Determines the minimum number of seconds + after parting that the bot will not herald the person when he or she + rejoins.""")) + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78 diff --git a/plugins/Herald/plugin.py b/plugins/Herald/plugin.py new file mode 100644 index 000000000..2470a7600 --- /dev/null +++ b/plugins/Herald/plugin.py @@ -0,0 +1,212 @@ +### +# Copyright (c) 2002-2004, 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. +### + +import os +import time +import getopt + +import supybot.log as log +import supybot.conf as conf +import supybot.utils as utils +import supybot.world as world +import supybot.ircdb as ircdb +from supybot.commands import * +import supybot.ircmsgs as ircmsgs +import supybot.plugins as plugins +import supybot.ircutils as ircutils +import supybot.registry as registry +import supybot.callbacks as callbacks + +filename = conf.supybot.directories.data.dirize('Herald.db') + +class HeraldDB(plugins.ChannelUserDB): + def serialize(self, v): + return [v] + + def deserialize(self, channel, id, L): + if len(L) != 1: + raise ValueError + return L[0] + +class Herald(callbacks.Privmsg): + def __init__(self): + self.__parent = super(Herald, self) + self.__parent.__init__() + self.db = HeraldDB(filename) + world.flushers.append(self.db.flush) + self.lastParts = plugins.ChannelUserDictionary() + self.lastHerald = plugins.ChannelUserDictionary() + + def die(self): + if self.db.flush in world.flushers: + world.flushers.remove(self.db.flush) + self.db.close() + self.__parent.die() + + def doJoin(self, irc, msg): + if ircutils.strEqual(irc.nick, msg.nick): + return # It's us. + channel = msg.args[0] + irc = callbacks.SimpleProxy(irc, msg) + if self.registryValue('heralding', channel): + try: + id = ircdb.users.getUserId(msg.prefix) + herald = self.db[channel, id] + except KeyError: + default = self.registryValue('default', channel) + if default: + default = ircutils.standardSubstitute(irc, msg, default) + msgmaker = ircmsgs.privmsg + if self.registryValue('default.notice', channel): + msgmaker = ircmsgs.notice + target = msg.nick + if self.registryValue('default.public', channel): + target = channel + irc.queueMsg(msgmaker(target, default)) + return + now = time.time() + throttle = self.registryValue('throttleTime', channel) + if now - self.lastHerald.get((channel, id), 0) > throttle: + if (channel, id) in self.lastParts: + i = self.registryValue('throttleTimeAfterPart', channel) + if now - self.lastParts[channel, id] < i: + return + self.lastHerald[channel, id] = now + herald = ircutils.standardSubstitute(irc, msg, herald) + irc.reply(herald, prefixName=False) + + def doPart(self, irc, msg): + try: + id = self._getId(irc, msg.prefix) + self.lastParts[msg.args[0], id] = time.time() + except KeyError: + pass + + def _getId(self, irc, userNickHostmask): + try: + id = ircdb.users.getUserId(userNickHostmask) + except KeyError: + if not ircutils.isUserHostmask(userNickHostmask): + hostmask = irc.state.nickToHostmask(userNickHostmask) + id = ircdb.users.getUserId(hostmask) + else: + raise KeyError + return id + + def default(self, irc, msg, args, channel, optlist, text): + """[] [--remove|] + + If is given, sets the default herald to . A of "" + will remove the default herald. If is not given, returns the + current default herald. is only necessary if the message + isn't sent in the channel itself. + """ + if optlist and text: + raise callbacks.ArgumentError + for (option, _) in optlist: + if option == 'remove': + self.setRegistryValue('default', '', channel) + irc.replySuccess() + return + if text: + self.setRegistryValue('default', text, channel) + irc.replySuccess() + else: + resp = self.registryValue('default', channel) or \ + 'I do not have a default herald set for %s.' % channel + irc.reply(resp) + default = wrap(default, ['channel', + getopts({'remove': ''}), + additional('text')]) + + def get(self, irc, msg, args, channel, user): + """[] [] + + Returns the current herald message for (or the user + is currently identified or recognized as). If + is not given, defaults to the user giving the command. + is only necessary if the message isn't sent in the channel itself. + """ + try: + herald = self.db[channel, user.id] + irc.reply(herald) + except KeyError: + irc.error('I have no herald for %s.' % user.name) + get = wrap(get, ['channel', first('otherUser', 'user')]) + + # I chose not to make optional in this command because + # if it's not a valid username (e.g., if the user tyops and misspells a + # username), it may be nice not to clobber the user's herald. + def add(self, irc, msg, args, channel, user, herald): + """[] + + Sets the herald message for (or the user is + currently identified or recognized as) to . is only + necessary if the message isn't sent in the channel itself. + """ + self.db[channel, user.id] = herald + irc.replySuccess() + add = wrap(add, ['channel', 'otherUser', 'text']) + + def remove(self, irc, msg, args, channel, user): + """[] [] + + Removes the herald message set for , or the user + is currently identified or recognized as. If + is not given, defaults to the user giving the command. + is only necessary if the message isn't sent in the channel + itself. + """ + try: + del self.db[channel, user.id] + irc.replySuccess() + except KeyError: + irc.error('I have no herald for that user.') + remove = wrap(remove, ['channel', first('otherUser', 'user')]) + + def change(self, irc, msg, args, channel, user, changer): + """[] [] + + Changes the herald message for , or the user is + currently identified or recognized as, according to . If + is not given, defaults to the calling user. is only + necessary if the message isn't sent in the channel itself. + """ + s = self.db[channel, user.id] + newS = changer(s) + self.db[channel, user.id] = newS + irc.replySuccess() + change = wrap(change, ['channel', + first('otherUser', 'user'), + 'regexpReplacer']) + + +Class = Herald + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/plugins/Herald/test.py b/plugins/Herald/test.py new file mode 100644 index 000000000..fb98ee199 --- /dev/null +++ b/plugins/Herald/test.py @@ -0,0 +1,37 @@ +### +# Copyright (c) 2002-2004, 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. +### + +from supybot.test import * + +class HeraldTestCase(PluginTestCase): + plugins = ('Herald',) + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: + diff --git a/setup.py b/setup.py index 76b41190a..445ca9bd6 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ plugins = [ 'Dict', 'Filter', 'Format', + 'Herald', 'Math', 'Misc', 'Owner',