Added PackageInfo plugin.

Moved package info/lookup to PackageInfo plugin
This commit is contained in:
Terence Simpson 2008-10-22 18:31:40 +01:00
parent 39044a2c16
commit 885ad30ded
11 changed files with 420 additions and 93 deletions

View File

@ -13,14 +13,14 @@
###
"""
This plugin is a factoid encyclopedia and has Ubuntu/Debian package&file lookup
funtionality
This plugin is a factoid encyclopedia. Ubuntu/Debian package&file lookup
funtionality has been moved to PackageInfo
"""
import supybot
import supybot.world as world
__version__ = "2.2"
__version__ = "2.3"
__author__ = supybot.Author("Dennis Kaarsemaker","Seveas","dennis@kaarsemaker.net")
__contributors__ = {supybot.Author("Terence Simpson", "stdin", "stdin@stdin.me.uk"): ['sync']}
__url__ = 'https://bots.ubuntulinux.nl'
@ -35,9 +35,3 @@ if world.testing:
Class = plugin.Class
configure = config.configure
def reloadPlugin():
reload(config)
reload(plugin)
Class = plugin.Class
configure = config.configure

View File

@ -22,23 +22,17 @@ def configure(advanced):
Encyclopedia = conf.registerPlugin('Encyclopedia')
conf.registerChannelValue(Encyclopedia, 'database',
registry.String('ubuntu', 'Name of database to use'))
conf.registerGlobalValue(Encyclopedia, 'packagelookup',
registry.Boolean(True, "Whether to look up packages"))
conf.registerChannelValue(Encyclopedia, 'relaychannel',
registry.String('#ubuntu-ops', 'Relay channel for unauthorized edits'))
conf.registerGlobalValue(Encyclopedia, 'notfoundmsg',
registry.String('Factoid %s not found', 'Reply when factoid isn\'t found'))
conf.registerChannelValue(Encyclopedia,'prefixchar',
registry.String('!','Prefix character for factoid display/editing'))
conf.registerChannelValue(Encyclopedia, 'searchorder',
registry.String('','Distro search order'))
conf.registerGlobalValue(Encyclopedia, 'datadir',
registry.String('', 'Path to dir containing factoid databases',private=True))
conf.registerGlobalValue(Encyclopedia, 'aptdir',
registry.String('', 'Path to apt cache directory',private=True))
registry.String("", 'Path to dir containing factoid databases', private=True))
conf.registerChannelValue(Encyclopedia, 'alert',
registry.String(['ops' 'op', 'kops', 'calltheops'],'factoid name(s) used for alerts'))
registry.SpaceSeparatedListOfStrings(['ops', 'op', 'kops', 'calltheops'], 'factoid name(s) used for alerts', private=True))
conf.registerChannelValue(Encyclopedia, 'remotedb',
registry.String('http://jussi01.com/web/ubuntu.db', 'Remote location of the master database'))
registry.String('http://jussi01.com/web/ubuntu.db', 'Remote location of the master database', private=True))
conf.registerChannelValue(Encyclopedia, 'ignores',
registry.SpaceSeparatedListOfStrings(['find', 'info'], 'factoid name(s) to ignore', private=True))

View File

@ -20,19 +20,41 @@ import supybot.registry as registry
import supybot.ircdb as ircdb
import supybot.conf as conf
import supybot.utils as utils
import sys, os
import supybot.ircutils as ircutils
import sys, os, re, md5, random, time
if sys.version_info >= (2, 5, 0):
import re
else:
import sre as re
try:
import packages
reload(packages)
have_packages = True
except ImportError:
have_packages = False
def checkIgnored(hostmask, recipient='', users=ircdb.users, channels=ircdb.channels):
if ircdb.ignores.checkIgnored(hostmask):
return True
try:
id = ircdb.users.getUserId(hostmask)
user = users.getUser(id)
except KeyError:
# If there's no user...
if ircutils.isChannel(recipient):
channel = channels.getChannel(recipient)
if channel.checkIgnored(hostmask):
return True
else:
return False
else:
return False
if user._checkCapability('owner'):
# Owners shouldn't ever be ignored.
return False
elif user.ignore:
return True
elif recipient:
if ircutils.isChannel(recipient):
channel = ircdb.channels.getChannel(recipient)
if channel.checkIgnored(hostmask):
return True
else:
return False
else:
return False
else:
return False
# Simple wrapper class for factoids
class Factoid:
@ -91,6 +113,14 @@ def capab(prefix, capability):
else:
return False
def safeQuote(s):
if isinstance(s, list):
res = []
for i in s:
res.append(safeQuote(i))
return res
return s.replace('%', '%%')
class Encyclopedia(callbacks.Plugin):
"""!factoid: show factoid"""
threaded = True
@ -98,13 +128,6 @@ class Encyclopedia(callbacks.Plugin):
def __init__(self, irc):
callbacks.Plugin.__init__(self, irc)
self.databases = {}
self.times = {}
self.seens = {}
self.distros = []
if have_packages:
self.Apt = packages.Apt(self)
else:
self.log.warning("Faild to import packages, you probably don't have python-apt installed")
self.edits = {}
self.alert = False
@ -139,15 +162,15 @@ class Encyclopedia(callbacks.Plugin):
removeeditor = wrap(removeeditor, ['text'])
def editors(self, irc, msg, args):
"""takes no arguments
"""Takes no arguments
Lists all the users who are in the list of editors.
"""
irc.reply(', '.join([u.name for u in ircdb.users.users.values() if capab(u.name, "editfactoids")]), private=True)
irc.reply(', '.join([u.name for u in ircdb.users.users.values() if capab(u.name, 'editfactoids')]), private=True)
editors = wrap(editors)
def moderators(self, irc, msg, args):
"""takes no arguments
"""Takes no arguments
Lists all the users who can add users to the list of editors.
"""
@ -218,7 +241,7 @@ class Encyclopedia(callbacks.Plugin):
return text[nlen+1:]
return False
else: # Private
if text.strip()[0] in str(conf.supybot.reply.whenAddressedBy.chars):
if text.strip()[0] in str(conf.supybot.reply.whenAddressedBy.chars.get(recipients)):
return False
if not text.split()[0] == 'search':
for c in irc.callbacks:
@ -261,7 +284,7 @@ class Encyclopedia(callbacks.Plugin):
return Factoid(f[0],f[1],f[2],f[3],f[4])
def resolve_alias(self, channel, factoid, loop=0):
if factoid and factoid.name == self.registryValue('alert',channel):
if factoid and factoid.name in self.registryValue('alert', channel):
self.alert = True
if loop >= 10:
return Factoid('','<reply> Error: infinite <alias> loop detected','','',0)
@ -324,16 +347,32 @@ class Encyclopedia(callbacks.Plugin):
return alias.value.lower
factoid.value = '<alias> ' + alias.name
def callPrecedence(self, irc):
before = []
for cb in irc.callbacks:
if cb.name() == 'IRCLogin':
before.append(cb)
return (before, [])
def doPrivmsg(self, irc, msg):
def beginswith(text, strings):
for string in strings:
if text.startswith(string):
return True
return False
# Filter CTCP
if chr(1) in msg.args[1]:
return
if checkIgnored(msg.prefix,msg.args[0]):
return
# Are we being queried?
recipient, text = msg.args
text = self.addressed(recipient, text, irc)
if not text:
return
doChanMsg = True
display_info = False
target = msg.args[0]
if target[0] != '#':
@ -351,25 +390,24 @@ class Encyclopedia(callbacks.Plugin):
# Now switch between actions
orig_text = text
lower_text = text.lower()
if "please see" in lower_text:
if "from %s" % irc.nick.lower() in lower_text or "from the bot" in lower_text:
doChanMsg = False
ret = ''
retmsg = ''
term = self.get_target(msg.nick, orig_text, target)
if term[0] == "info":
ret = "Retrieve information on a package: !info <package>"
retmsg = term[2]
elif term[0] == "find":
ret = "Search for a pacakge or a file: !find <term/file>"
retmsg = term[2]
elif term[0] == "search":
if term[0] == "search":
ret = "Search factoids for term: !search <term>"
retmsg = term[2]
elif term[0] == "seen":
elif term[0] == "seen" or term[0].startswith("seen "):
ret = "I have no seen command"
retmsg = term[2] and "%s: " % msg.prefix.split('!', 1)[0] or ''
elif term[0] in ("what", "whats", "what's") or term[0].startswith("what ") or term[0].startswith("what ") or term[0].startswith("whats ") or term[0].startswith("what's "): # Try and catch people saying "what is ...?"
ret = "I am only a bot, please don't think I'm intelligent :)"
retmsg = term[2]
elif lower_text[:5] not in ('info ','find '):
elif beginswith(lower_text, self.registryValue('ignores', channel)):
return
else:
# Lookup, search or edit?
if lower_text.startswith('search '):
ret = self.search_factoid(lower_text[7:].strip(), channel)
@ -408,22 +446,8 @@ class Encyclopedia(callbacks.Plugin):
text, target, retmsg = self.get_target(msg.nick, orig_text, target)
if text.startswith('bug ') and text != ('bug 1'):
return
#if text == self.registryValue('alert') and msg.args[0][0] == '#' and not display_info:
# msg.tag('alert')
ret = self.factoid_lookup(text, channel, display_info)
# Fall through to package lookup
if have_packages and self.registryValue('packagelookup') and (not ret or not len(ret)):
text, target, retmsg = self.get_target(msg.nick, orig_text.lower(), target)
if text.startswith('info '):
ret = self.Apt.info(text[5:].strip(),self.registryValue('searchorder', channel).split())
elif text.startswith('find '):
ret = self.Apt.find(orig_text[5:].strip(),self.registryValue('searchorder', channel).split())
#else:
# ret = self.Apt.info(text.strip(),self.registryValue('searchorder', channel).split())
# if ret.startswith('Package'):
# ret = None
if not ret:
if len(text) > 15:
irc.error("I am only a bot, please don't think I'm intelligent :)")
@ -432,18 +456,16 @@ class Encyclopedia(callbacks.Plugin):
ret = self.registryValue('notfoundmsg')
if ret.count('%') == ret.count('%s') == 1:
ret = ret % repr(text)
if channel.lower() != irc.nick.lower() and target[0] != '#': # not /msg
if doChanMsg and channel.lower() != irc.nick.lower() and target[0] != '#': # not /msg
if target in irc.state.channels[channel].users:
queue(irc, channel, "%s, please see my private message" % target)
if type(ret) != list:
queue(irc, target, retmsg + ret)
else:
queue(irc, target, retmsg + ret[0])
#if msg.tagged('alert'):
if self.alert:
if target.startswith('#') and not target.endswith('bots'):
queue(irc, self.registryValue('relayChannel',channel), '%s called the ops in %s (%s)' % (msg.nick, msg.args[0], retmsg[:-2]))
#queue(irc, self.registryValue('relayChannel'), retmsg + ret[0])
queue(irc, self.registryValue('relayChannel', channel), '%s called the ops in %s (%s)' % (msg.nick, msg.args[0], retmsg[:-2]))
self.alert = False
for r in ret[1:]:
queue(irc, target, r)
@ -616,18 +638,25 @@ class Encyclopedia(callbacks.Plugin):
"""[<channel>]
Downloads a copy of the database from the remote server.
Set the server with the channel configuration variable supybot.plugins.Encyclopedia.remotedb.
If <channel> is not set it will default to the channel the command is given in or the global value
Set the server with the channel variable supybot.plugins.Encyclopedia.remotedb.
If <channel> is not set it will default to the channel the command is given in or the global value.
"""
if not capab(msg.prefix, "owner"):
irc.error("Sorry, you can't do that")
return
if channel:
if not ircutils.isChannel(channel):
irc.error("'%s' is not a valid channel" % safeQuote(channel))
return
remotedb = self.registryValue('remotedb', channel)
if not remotedb:
return
def download_database(location, dpath):
"""Download the database located at location to path dpath"""
import urllib2
tmp_db = "%s%stmp" % (dpath, os.extsep)
fd = urllib2.urlopen(location)
fd2 = open(tmp_db,'w')
fd2 = open(tmp_db, 'w')
fd2.write(fd.read()) # Download to a temparary file
fd.close()
fd2.close()
@ -636,23 +665,24 @@ class Encyclopedia(callbacks.Plugin):
data = fd2.read(47)
if data == '** This file contains an SQLite 2.1 database **': # OK, rename to dpath
os.rename(tmp_db, dpath)
self.databases[channel].close()
self.databases.pop(channel)
else: # Remove the tmpparary file and raise an error
os.remove(tmp_db)
raise RuntimeError, "Downloaded file was not a SQLite 2.1 database"
db = self.registryValue('database', channel)
rdb = self.registryValue('remotedb', channel)
if not db:
if channel:
irc.error("I don't have a database set for %s" % channel)
return
irc.error("There is no global database set, use 'config supybot.plugins.Encyclopedia.database <database>' to set it")
return
if not rdb:
if not remotedb:
if channel:
irc.error("I don't have a remote database set for %s" % channel)
return
irc.error("There is no global remote database set, use 'config supybot.plugins.Encyclopedia.remotedb <url>' ro set it")
irc.error("There is no global remote database set, use 'config supybot.plugins.Encyclopedia.remotedb <url>' to set it")
return
dbpath = os.path.join(self.registryValue('datadir'), '%s.db' % db)
# We're moving files and downloading, lots can go wrong so use lots of try blocks.
@ -667,14 +697,15 @@ class Encyclopedia(callbacks.Plugin):
try:
# Downloading can take some time, let the user know we're doing something
irc.reply("Attemting to download database", prefixNick=False)
download_database(rdb, dbpath)
download_database(remotedb, dbpath)
irc.replySuccess()
except Exception, e:
self.log.error("Could not download %s to %s" % (rdb, dbpath))
self.log.error("Could not download %s to %s" % (remotedb, dbpath))
self.log.error(utils.exnToString(e))
irc.error("Internal error, see log")
os.rename("%s.backup" % dbpath, dbpath)
return
sync = wrap(sync, [optional("somethingWithoutSpaces")])
Class = Encyclopedia

1
PackageInfo/README.txt Normal file
View File

@ -0,0 +1 @@
Insert a description of your plugin here, with any notes, etc. about using it.

68
PackageInfo/__init__.py Normal file
View File

@ -0,0 +1,68 @@
###
# Copyright (c) 2008, Terence Simpson
# 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.
###
"""
Add a description of the plugin (to be presented to the user inside the wizard)
here. This should describe *what* the plugin does.
"""
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__ = "0.0.1"
# XXX Replace this with an appropriate author or supybot.Author instance.
__author__ = supybot.Author("Terence Simpson", "stdin", "stdin@stdin.me.uk")
# This is a dictionary mapping supybot.Author instances to lists of
# contributions.
__contributors__ = {supybot.Author("Dennis Kaarsemaker","Seveas","dennis@kaarsemaker.net"): ["Origional concept"]}
# This is a url where the most recent plugin package can be downloaded.
__url__ = '' # 'http://supybot.com/Members/yourname/PackageInfo/download'
import config
import plugin
reload(plugin) # In case we're being reloaded.
import packages
reload(packages)
# 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=4 expandtab textwidth=79:

70
PackageInfo/config.py Normal file
View File

@ -0,0 +1,70 @@
###
# Copyright (c) 2008, Terence Simpson
# 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 output, expect, anything, something, yn
conf.registerPlugin('PackageInfo', True)
enabled = yn("Enable the pugin", default=True)
if not enabled:
PackageInfo.enabled.setValue(enabled)
PackageInfo.aptdir.setValue('')
PackageInfo.prefixchar.setValue('!')
PackageInfo.defaultRelease.setValue("hardy")
return
aptdir = something("Where should the apt directory be? (<botdir>/data/apt for example)")
output("This value should be different from the bots default reply character")
prefixchar = something("What character should the bot respond to?", default='!')
defaultRelease = expect("Default release to use when none is specified",
possibilities=['dapper', 'feisty', 'gutsy', 'hardy', 'intrepid'],
default='hardy')
PackageInfo.enabled.setValue(enabled)
PackageInfo.aptdir.setValue(aptdir)
PackageInfo.prefixchar.setValue(prefixchar)
PackageInfo.defaultRelease.setValue(defaultRelease)
PackageInfo = conf.registerPlugin('PackageInfo')
conf.registerChannelValue(PackageInfo, 'enabled',
registry.Boolean(True, "Enable package lookup"))
conf.registerChannelValue(PackageInfo, 'prefixchar',
conf.ValidPrefixChars('!', "Character the bot will respond to"))
conf.registerChannelValue(PackageInfo, 'defaultRelease',
registry.String('', "Default release to use when none is specified"))
conf.registerGlobalValue(PackageInfo, 'aptdir',
registry.String('', "Path to the apt directory", private=True))
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -23,8 +23,10 @@ class Apt:
self.aptdir = plugin.registryValue('aptdir')
self.distros = []
self.plugin = plugin
self.log = plugin.log
if self.aptdir:
self.distros = [x[:-5] for x in os.listdir(self.aptdir) if x.endswith('.list')]
self.distros.sort()
self.aptcommand = """apt-cache\\
-o"Dir::State::Lists=%s/%%s"\\
-o"Dir::etc::sourcelist=%s/%%s.list"\\
@ -35,11 +37,11 @@ class Apt:
def find(self, pkg, checkdists, filelookup=True):
_pkg = ''.join([x for x in pkg.strip().split(None,1)[0] if x.isalnum or x in '.-_+'])
distro = checkdists[0]
distro = checkdists
if len(pkg.strip().split()) > 1:
distro = ''.join([x for x in pkg.strip().split(None,2)[1] if x.isalnum or x in '.-_+'])
if distro not in self.distros:
distro = checkdists[0]
return "%s is not a valid distribution %s" % (distro, self.distros)
pkg = _pkg
data = commands.getoutput(self.aptcommand % (distro, distro, distro, 'search -n', pkg))
@ -48,10 +50,10 @@ class Apt:
data = commands.getoutput(self.aptfilecommand % (distro, distro, pkg)).split()
if data:
if data[0] == 'sh:': # apt-file isn't installed
self.plugin.log.error("apt-file is not installed")
self.log.error("apt-file is not installed")
return "Please use http://packages.ubuntu.com/ to search for files"
if data[0] == 'E:': # No files in the cache dir
self.plugin.log.error("Please run the 'update_apt_file' script")
self.log.error("Please run the 'update_apt_file' script")
return "Cache out of date, please contact the administrator"
if len(data) > 5:
return "File %s found in %s (and %d others)" % (pkg, ', '.join(data[:5]), len(data)-5)
@ -69,14 +71,16 @@ class Apt:
distro = None
if len(pkg.strip().split()) > 1:
distro = ''.join([x for x in pkg.strip().split(None,2)[1] if x.isalnum() or x in '-._+'])
if distro:
if distro not in self.distros:
checkdists = [checkdists[0]]
else:
checkdists = [distro]
if not distro:
distro = checkdists
if distro not in self.distros:
return "%s is not a valid distribution %s" % (distro, self.distros)
checkdists = distro
pkg = _pkg
for distro in checkdists:
for distro in [checkdists]:
data = commands.getoutput(self.aptcommand % (distro, distro, distro, 'show', pkg))
data2 = commands.getoutput(self.aptcommand % (distro, distro, distro, 'showsrc', pkg))
if not data or 'E: No packages found' in data:
@ -90,7 +94,7 @@ class Apt:
parser.feed(p)
p = parser.close()
if type(p) == type(""):
self.plugin.log.error("apt returned an error, do you have the deb-src URLs in %s.list" % distro)
self.log.error("apt returned an error, do you have the deb-src URLs in %s.list?" % distro)
return "Package lookup faild"
if apt.VersionCompare(maxp['Version'], p['Version']) < 0:
maxp = p
@ -104,7 +108,7 @@ class Apt:
parser.feed(p)
p = parser.close()
if type(p) == type(""):
self.plugin.log.error("apt returned an error, do you have the deb-src URLs in %s.list" % distro)
self.log.error("apt returned an error, do you have the deb-src URLs in %s.list?" % distro)
return "Package lookup faild"
if apt.VersionCompare(maxp2['Version'], p['Version']) < 0:
maxp2 = p

128
PackageInfo/plugin.py Normal file
View File

@ -0,0 +1,128 @@
###
# Copyright (c) 2008, Terence Simpson
# 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.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
import supybot.ircutils as ircutils
import supybot.conf as conf
import os
import packages
reload(packages)
class PackageInfo(callbacks.Plugin):
"""Lookup package information via apt-cache/apt-file"""
threaded = True
def __init__(self, irc):
self.__parent = super(PackageInfo, self)
self.__parent.__init__(irc)
self.Apt = packages.Apt(self)
def callPrecedence(self, irc):
before = []
for cb in irc.callbacks:
if cb.name() == 'IRCLogin':
before.append(cb)
return (before, [])
def __getRelease(self, irc, release, channel, doError=True):
if release:
return release
release = self.registryValue("defaultRelease", channel)
if not release:
if doError:
irc.error("'supybot.plugins.PackageInfo.defaultRelease' is not set")
return None
return release
def __getChannel(self, channel):
return ircutils.isChannel(channel) and channel or None
def info(self, irc, msg, args, package, release):
"""<package> [<release>]
Lookup information for <package>, optionally in <release>
"""
channel = self.__getChannel(msg.args[0])
release = self.__getRelease(irc, release, channel)
if not release:
return
irc.reply(self.Apt.info(package, release))
info = wrap(info, ['text', optional('text')])
def find(self, irc, msg, args, package, release):
"""<package/filename> [<release>]
Search for <package> or, of that fails, find <filename>'s package(s).
Optionally in <release>
"""
channel = self.__getChannel(msg.args[0])
release = self.__getRelease(irc, release, channel)
if not release:
return
irc.reply(self.Apt.find(package, release))
find = wrap(find, ['text', optional('text')])
def doPrivmsg(self, irc, msg):
channel = self.__getChannel(msg.args[0])
if not channel:
return
if not self.registryValue("enabled", channel):
return
release = self.__getRelease(irc, None, channel, False)
if not release:
return
if chr(1) in msg.args[1]: # CTCP
return
text = callbacks.addressed(irc.nick.lower(), msg, prefixChars=self.registryValue("prefixchar", channel))
if not text:
return
if text[0] in str(conf.supybot.reply.whenAddressedBy.get('chars')):
return
if text[0] == self.registryValue("prefixchar", channel):
text = text[1:]
if text.lower()[:4] not in ("find", "info"):
return
if text.lower()[:4] == "find":
irc.reply(self.Apt.find(text[4:].strip(), self.registryValue("defaultRelease", channel)))
else:
irc.reply(self.Apt.info(text[4:].strip(), self.registryValue("defaultRelease", channel)))
Class = PackageInfo
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

37
PackageInfo/test.py Normal file
View File

@ -0,0 +1,37 @@
###
# Copyright (c) 2008, Terence Simpson
# 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 PackageInfoTestCase(PluginTestCase):
plugins = ('PackageInfo',)
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -1,6 +1,6 @@
#!/bin/bash
DIR=/home/dennis/ubotu/data/apt
DIR=/home/stdin/bot/data/apt
for DIST in "$DIR"/*.list; do
test -h $DIST && continue

View File

@ -1,6 +1,6 @@
#!/bin/bash
DIR=/home/dennis/ubotu/data/apt
DIR=/home/stdin/bot/data/apt
for DIST in "$DIR"/*.list; do
test -h $DIST && continue