diff --git a/plugins/ShrinkUrl/README.txt b/plugins/ShrinkUrl/README.txt new file mode 100644 index 000000000..d60b47a97 --- /dev/null +++ b/plugins/ShrinkUrl/README.txt @@ -0,0 +1 @@ +Insert a description of your plugin here, with any notes, etc. about using it. diff --git a/plugins/ShrinkUrl/__init__.py b/plugins/ShrinkUrl/__init__.py new file mode 100644 index 000000000..a9ad70a18 --- /dev/null +++ b/plugins/ShrinkUrl/__init__.py @@ -0,0 +1,58 @@ +### +# 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. +### + +""" +Shrinks URLs using tinyurl.com and ln-s.net. +""" + +import supybot +import supybot.world as world + +__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 tabstop=8 expandtab textwidth=78: diff --git a/plugins/ShrinkUrl/config.py b/plugins/ShrinkUrl/config.py new file mode 100644 index 000000000..2e4a833f2 --- /dev/null +++ b/plugins/ShrinkUrl/config.py @@ -0,0 +1,68 @@ +### +# 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. +### + +import supybot.conf as conf +import supybot.registry as registry + +def configure(advanced): + from supybot.questions import output, expect, anything, something, yn + conf.registerPlugin('ShrinkUrl', True) + if yn("""This plugin offers a snarfer that will go retrieve a shorter + version of long URLs that are sent to the channel. Would you + like this snarfer to be enabled?""", default=False): + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True) + +class ShrinkService(registry.OnlySomeStrings): + validStrings = ('ln', 'tiny') + +ShrinkUrl = conf.registerPlugin('ShrinkUrl') +conf.registerChannelValue(ShrinkUrl, 'shrinkSnarfer', + registry.Boolean(False, """Determines whether the + shrink snarfer is enabled. This snarfer will watch for URLs in the + channel, and if they're sufficiently long (as determined by + supybot.plugins.ShrinkUrl.minimumLength) it will post a + smaller URL from either ln-s.net or tinyurl.com, as denoted in + supybot.plugins.ShrinkUrl.default.""")) +conf.registerChannelValue(ShrinkUrl, 'minimumLength', + registry.PositiveInteger(48, """The minimum length a URL must be before + the bot will shrink it.""")) +conf.registerChannelValue(ShrinkUrl, 'nonSnarfingRegexp', + registry.Regexp(None, """Determines what URLs are to be snarfed; URLs + matching the regexp given will not be snarfed. Give the empty string if + you have no URLs that you'd like to exclude from being snarfed.""")) +conf.registerChannelValue(ShrinkUrl, 'outFilter', + registry.Boolean(False, """Determines whether the bot will shrink the URLs + of outgoing messages if those URLs are longer than + supybot.plugins.ShrinkUrl.minimumLength.""")) +conf.registerChannelValue(ShrinkUrl, 'default', + ShrinkService('ln', """Determines what website the bot will use when + shrinking a URL.""")) + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78 diff --git a/plugins/ShrinkUrl/plugin.py b/plugins/ShrinkUrl/plugin.py new file mode 100644 index 000000000..3e58d7d63 --- /dev/null +++ b/plugins/ShrinkUrl/plugin.py @@ -0,0 +1,209 @@ +### +# 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 re + +import supybot.conf as conf +import supybot.utils as utils +from supybot.commands import * +import supybot.ircmsgs as ircmsgs +import supybot.plugins as plugins +import supybot.ircutils as ircutils +import supybot.callbacks as callbacks + +class CdbShrunkenUrlDB(object): + def __init__(self, filename): + self.tinyDb = conf.supybot.databases.types.cdb.connect( + filename.replace('.db', '.Tiny.db')) + self.lnDb = conf.supybot.databases.types.cdb.connect( + filename.replace('.db', '.Ln.db')) + + def getTiny(self, url): + return self.tinyDb[url] + + def setTiny(self, url, tinyurl): + self.tinyDb[url] = tinyurl + + def getLn(self, url): + return self.lnDb[url] + + def setLn(self, url, lnurl): + self.lnDb[url] = lnurl + + def close(self): + self.tinyDb.close() + self.lnDb.close() + + def flush(self): + self.tinyDb.flush() + self.lnDb.flush() + +ShrunkenUrlDB = plugins.DB('ShrinkUrl', {'cdb': CdbShrunkenUrlDB}) + +class ShrinkUrl(callbacks.PrivmsgCommandAndRegexp): + regexps = ['shrinkSnarfer'] + def __init__(self, irc): + self.__parent = super(ShrinkUrl, self) + self.__parent.__init__(irc) + self.db = ShrunkenUrlDB() + + def die(self): + self.db.close() + + def callCommand(self, name, irc, msg, *L, **kwargs): + try: + self.__parent.callCommand(name, irc, msg, *L, **kwargs) + except utils.web.Error, e: + irc = callbacks.SimpleProxy(irc, msg) + irc.error(str(e)) + + def _outFilterThread(self, irc, msg): + (channel, text) = msg.args + for m in utils.web.httpUrlRe.finditer(text): + url = m.group(1) + if len(url) > self.registryValue('minimumLength', channel): + cmd = self.registryValue('default', channel) + try: + if cmd == 'ln': + (shortUrl, _) = self._getLnUrl(url) + elif cmd == 'tiny': + shortUrl = self._getTinyUrl(url) + text = text.replace(url, shortUrl) + except utils.web.Error: + pass + newMsg = ircmsgs.privmsg(channel, text, msg=msg) + newMsg.tag('shrunken') + irc.queueMsg(newMsg) + + def outFilter(self, irc, msg): + channel = msg.args[0] + if msg.command == 'PRIVMSG' and irc.isChannel(channel): + if not msg.shrunken: + if self.registryValue('outFilter', channel): + if utils.web.httpUrlRe.search(msg.args[1]): + self._outFilterThread(irc, msg) + return None + return msg + + def shrinkSnarfer(self, irc, msg, match): + r"https?://[^\])>\s]{13,}" + channel = msg.args[0] + if not irc.isChannel(channel): + return + if self.registryValue('shrinkSnarfer', channel): + url = match.group(0) + r = self.registryValue('nonSnarfingRegexp', channel) + if r and r.search(url) is not None: + self.log.debug('Matched nonSnarfingRegexp: %s', + utils.quoted(url)) + return + minlen = self.registryValue('minimumLength', channel) + cmd = self.registryValue('default', channel) + if len(url) >= minlen: + shorturl = None + if cmd == 'tiny': + shorturl = self._getTinyUrl(url) + elif cmd == 'ln': + (shorturl, _) = self._getLnUrl(url) + if shorturl is None: + self.log.info('Couldn\'t get shorturl for %s', + utils.quoted(url)) + return + domain = utils.web.getDomain(url) + s = '%s (at %s)' % (ircutils.bold(shorturl), domain) + m = irc.reply(s, prefixName=False) + m.tag('shrunken') + shrinkSnarfer = urlSnarfer(shrinkSnarfer) + + def _getLnUrl(self, url): + url = utils.web.urlquote(url) + try: + return (self.db.getLn(url), '200') + except KeyError: + text = utils.web.getUrl('http://ln-s.net/home/api.jsp?url=' + url) + (code, lnurl) = text.split(None, 1) + lnurl = lnurl.strip() + if code == '200': + self.db.setLn(url, lnurl) + else: + lnurl = None + return (lnurl, code) + + def ln(self, irc, msg, args, url): + """ + + Returns an ln-s.net version of . + """ + if len(url) < 17: + irc.error('Stop being a lazy-biotch and type the URL yourself.') + return + (lnurl, error) = self._getLnUrl(url) + if lnurl is not None: + domain = utils.web.getDomain(url) + m = irc.reply(lnurl) + m.tag('shrunken') + else: + irc.error(error) + ln = thread(wrap(ln, ['url'])) + + _tinyRe = re.compile(r'
(http://tinyurl\.com/\w+)') + def _getTinyUrl(self, url): + try: + return self.db.getTiny(url) + except KeyError: + s = utils.web.getUrl('http://tinyurl.com/create.php?url=' + url) + m = self._tinyRe.search(s) + if m is None: + tinyurl = None + else: + tinyurl = m.group(1) + self.db.setTiny(url, tinyurl) + return tinyurl + + def tiny(self, irc, msg, args, url): + """ + + Returns a TinyURL.com version of + """ + if len(url) < 20: + irc.error('Stop being a lazy-biotch and type the URL yourself.') + return + tinyurl = self._getTinyUrl(url) + if tinyurl is not None: + m = irc.reply(tinyurl) + m.tag('shrunken') + else: + s = 'Could not parse the TinyURL.com results page.' + irc.errorPossibleBug(s) + tiny = thread(wrap(tiny, ['url'])) + + +Class = ShrinkUrl + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/plugins/ShrinkUrl/test.py b/plugins/ShrinkUrl/test.py new file mode 100644 index 000000000..814797861 --- /dev/null +++ b/plugins/ShrinkUrl/test.py @@ -0,0 +1,120 @@ +### +# 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 ShrinkUrlTestCase(ChannelPluginTestCase): + plugins = ('ShrinkUrl',) + if network: + def testTinyurl(self): + try: + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(False) + self.assertRegexp( + 'shrinkurl tiny http://sourceforge.net/tracker/?' + 'func=add&group_id=58965&atid=489447', + r'http://tinyurl.com/rqac') + conf.supybot.plugins.ShrinkUrl.default.setValue('tiny') + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True) + self.assertRegexp( + 'shrinkurl tiny http://sourceforge.net/tracker/?' + 'func=add&group_id=58965&atid=489447', + r'http://tinyurl.com/rqac') + finally: + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(False) + + def testTinysnarf(self): + try: + conf.supybot.snarfThrottle.setValue(1) + conf.supybot.plugins.ShrinkUrl.default.setValue('tiny') + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True) + self.assertSnarfRegexp( + 'http://sourceforge.net/tracker/?func=add&' + 'group_id=58965&atid=489447', + r'http://tinyurl.com/rqac.* \(at') + self.assertSnarfRegexp( + 'http://www.urbandictionary.com/define.php?' + 'term=all+your+base+are+belong+to+us', + r'http://tinyurl.com/u479.* \(at') + finally: + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(False) + + def testLnurl(self): + try: + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(False) + self.assertRegexp( + 'shrinkurl ln http://sourceforge.net/tracker/?' + 'func=add&group_id=58965&atid=489447', + r'http://ln-s.net/25Z') + conf.supybot.plugins.ShrinkUrl.default.setValue('ln') + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True) + self.assertRegexp( + 'shrinkurl ln http://sourceforge.net/tracker/?' + 'func=add&group_id=58965&atid=489447', + r'http://ln-s.net/25Z') + finally: + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(False) + + def testLnsnarf(self): + try: + conf.supybot.snarfThrottle.setValue(1) + conf.supybot.plugins.ShrinkUrl.default.setValue('ln') + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True) + self.assertSnarfRegexp( + 'http://sourceforge.net/tracker/?func=add&' + 'group_id=58965&atid=489447', + r'http://ln-s.net/25Z.* \(at') + self.assertSnarfRegexp( + 'http://www.urbandictionary.com/define.php?' + 'term=all+your+base+are+belong+to+us', + r'http://ln-s.net/2\$K.* \(at') + finally: + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(False) + + def testNonSnarfing(self): + tiny = conf.supybot.plugins.ShrinkUrl.shrinkSnarfer() + snarf = conf.supybot.plugins.ShrinkUrl.nonSnarfingRegexp() + try: + conf.supybot.snarfThrottle.setValue(1) + conf.supybot.plugins.ShrinkUrl.default.setValue('tiny') + conf.supybot.plugins.ShrinkUrl.nonSnarfingRegexp.set('m/sf/') + conf.supybot.plugins.ShrinkUrl.minimumLength.setValue(10) + try: + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True) + self.assertSnarfNoResponse('http://sf.net/', 2) + self.assertSnarfRegexp('http://sourceforge.net/', + r'http://tinyurl.com/7vm7.* \(at') + finally: + conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(tiny) + finally: + conf.supybot.plugins.ShrinkUrl.nonSnarfingRegexp.setValue(snarf) + + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: + diff --git a/setup.py b/setup.py index d1eaa5ff6..489f3cb0c 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ plugins = [ 'Owner', 'QuoteGrabs', 'Scheduler', + 'ShrinkUrl', 'Status', 'User', 'Utilities',