Added PackageInfo plugin.
Moved package info/lookup to PackageInfo plugin
This commit is contained in:
parent
39044a2c16
commit
885ad30ded
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Insert a description of your plugin here, with any notes, etc. about using it.
|
|
@ -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:
|
|
@ -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:
|
|
@ -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
|
|
@ -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:
|
|
@ -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:
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue