PackageInfo: General overhaul.

* Add full support for Debian releases.
* Unify and simplify piping and redirection.
* Various general and minor improvements.
* Switch architecture for infos to amd64.
* Update default configuration.
* Improve APT update scripts.
This commit is contained in:
Krytarik Raido 2018-03-09 19:56:04 +01:00
parent 4956995f9a
commit 398247f0dd
7 changed files with 329 additions and 489 deletions

View File

@ -20,10 +20,10 @@ deb-src http://archive.ubuntu.com/ubuntu lucid main restricted universe multiver
supybot.plugins.PackageInfo.defaultRelease:
Set this to the default release to use when none is specified. (Channel)
Default: 'lucid'
Default: 'zesty'
Whenever you create a new .list file, it is important to run the update_apt
and update_apt_file scripts that comes with this plugin. Before you run these,
and update_apt_file scripts that come with this plugin. Before you run these,
you have to edit them to point to your apt dir. It's also useful to run them
periodically from cron (say, once per week for update_apt and once per moth for
update_apt_file). You also need to reload the plugin to make it pick up the new
@ -39,11 +39,14 @@ Default: !
--Usage--
find <package/filename> [<release>]
Search for <package> or, of that fails, find <filename>'s package(s).
Optionally in <release>
Search for <package> or, if that fails, <filename> in packages,
optionally in <release>
info <package> [<release>]
Lookup information for <package>, optionally in <release>
Look up information for <package>, optionally in <release>
if supybot.plugins.PackageInfo.enabled is True the bot will also reply to
depends <package> [<release>]
Look up dependencies for <package>, optionally in <release>
If supybot.plugins.PackageInfo.enabled is True the bot will also reply to
the commands if prefixed with supybot.plugins.PackageInfo.prefixchar.

View File

@ -22,7 +22,7 @@ import supybot
import supybot.world as world
from imp import reload
__version__ = "1.1.0"
__version__ = "1.2.0"
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
__contributors__ = {
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Concept'],

View File

@ -18,15 +18,24 @@ 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
# 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
# user or not. You should affect your configuration by manipulating the
# registry as appropriate.
def makeSource(release):
def makeSourceUbuntu(release):
return """deb http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
""" % (release, release)
#"""
def makeSourceDebian(release):
return """deb http://deb.debian.org/debian/ %s main contrib non-free
deb-src deb http://deb.debian.org/debian/ %s main contrib non-free
""" % (release, release)
def makeSourceDebianSec(release):
return """deb http://security.debian.org/ %s/updates main contrib non-free
deb-src deb http://security.debian.org/ %s/updates main contrib non-free
""" % (release, release)
from supybot.questions import output, expect, something, yn
import subprocess
@ -42,13 +51,13 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
enabled = yn("Enable this plugin in all channels?", default=True)
if enabled and advanced:
prefixchar = something("Which prefix character should be bot respond to?", default=PackageInfo.prefixchar._default)
defaultRelease = something("What should be the default distrobution when not specified?", default=PackageInfo.defaultRelease._default)
prefixchar = something("Which prefix character should the bot respond to?", default=PackageInfo.prefixchar._default)
defaultRelease = something("What should be the default release when none is specified?", default=PackageInfo.defaultRelease._default)
aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=PackageInfo.aptdir._default)
# People tend to thing this should be /var/cache/apt
# People tend to think this should be /var/cache/apt
while aptdir.startswith('/var'): #NOTE: This is not a good hack. Maybe just blacklist /var/cache/apt (or use apt to report back the cache dir)
output("NO! Do not use your systems apt directory")
output("NO! Do not use your system's apt directory")
aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=PackageInfo.aptdir._default)
else:
@ -62,7 +71,8 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
PackageInfo.prefixchar.setValue(prefixchar)
PackageInfo.defaultRelease.setValue(defaultRelease)
default_dists = set(['dapper', 'hardy', 'lucid', 'maveric', 'natty', 'oneiric'])
default_dists = set(['precise', 'trusty', 'xenial', 'yakkety', 'zesty', 'artful',
'oldstable', 'stable', 'unstable', 'testing', 'experimental'])
pluginDir = os.path.abspath(os.path.dirname(__file__))
update_apt = os.path.join(pluginDir, 'update_apt')
update_apt_file = os.path.join(pluginDir, 'update_apt_file')
@ -72,7 +82,7 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
## Create the aptdir
try:
os.makedirs(aptdir)
except OSError: # The error number would be OS dependant (17 on Linux 2.6, ?? on others). So just pass on this
except OSError: # The error number would be OS dependent (17 on Linux 2.6, ?? on others). So just pass on this
pass
for release in default_dists:
@ -80,19 +90,31 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
try:
output("Creating %s" % filename)
fd = open(filename, 'wb')
fd.write("# Apt sources list for Ubuntu %s\n" % release)
fd.write(makeSource(release))
fd.write(makeSource(release + '-security'))
fd.write(makeSource(release + '-updates'))
fd.write("# Apt sources list for %s\n" % release)
if release in ('oldstable', 'stable', 'unstable', 'testing', 'experimental'):
fd.write(makeSourceDebian(release))
if release in ('oldstable', 'stable', 'testing'):
fd.write(makeSourceDebian(release + '-updates'))
fd.write(makeSourceDebianSec(release))
else:
fd.write(makeSourceUbuntu(release))
fd.write(makeSourceUbuntu(release + '-updates'))
fd.write(makeSourceUbuntu(release + '-security'))
fd.close()
if release in ('unstable', 'experimental'):
continue
for sub in ('backports', 'proposed'):
sub_release = "%s-%s" % (release, sub)
filename = os.path.join(aptdir, "%s.list" % sub_release)
output("Creating %s" % filename)
fd = open(filename, 'wb')
fd.write("# Apt sources list for Ubuntu %s\n" % release)
fd.write(makeSource(sub_release))
fd.write("# Apt sources list for %s\n" % sub_release)
if release in ('oldstable', 'stable', 'testing'):
fd.write(makeSourceDebian(sub_release.replace('proposed', 'proposed-updates')))
else:
fd.write(makeSourceUbuntu(sub_release))
fd.close()
except Exception as e:
output("Error writing to %r: %r (%s)" % (filename, str(e), type(e)))
@ -116,7 +138,7 @@ conf.registerChannelValue(PackageInfo, 'enabled',
conf.registerChannelValue(PackageInfo, 'prefixchar',
conf.ValidPrefixChars('!', "Character the bot will respond to"))
conf.registerChannelValue(PackageInfo, 'defaultRelease',
registry.String('natty', "Default release to use when none is specified"))
registry.String('zesty', "Default release to use when none is specified"))
conf.registerGlobalValue(PackageInfo, 'aptdir',
conf.Directory(conf.supybot.directories.data.dirize('aptdir'), "Path to the apt directory", private=True))

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- Encoding: utf-8 -*-
###
# Copyright (c) 2006-2007 Dennis Kaarsemaker
@ -18,38 +17,39 @@
import warnings
warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
import subprocess, os, apt
import subprocess, os, apt, re
import supybot.utils as utils
from email.parser import FeedParser
def component(arg):
if '/' in arg: return arg[:arg.find('/')]
if '/' in arg:
return arg[:arg.find('/')]
return 'main'
def description(pkg):
if not pkg:
return None
if 'Description-en' in pkg:
return pkg['Description-en'].split('\n')[0]
elif 'Description' in pkg:
return pkg['Description'].split('\n')[0]
return None
return "Description not available"
def apt_cache(aptdir, distro, extra):
def apt_cache(aptdir, distro, cmd, pkg):
return subprocess.check_output(['apt-cache',
'-oAPT::Architecture=amd64',
'-oAPT::Architectures::=i386',
'-oAPT::Architectures::=amd64',
'-oDir::State::Lists=%s/%s' % (aptdir, distro),
'-oDir::State::Status=%s/%s.status' % (aptdir, distro),
'-oDir::Etc::SourceList=%s/%s.list' % (aptdir, distro),
'-oDir::Etc::SourceParts=""',
'-oDir::Cache=%s/cache' % aptdir,
'-oAPT::Architecture=i386'] +
extra).decode('utf8')
'-oDir::Cache=%s/cache' % aptdir] +
cmd + [pkg.lower()]).decode('utf8')
def apt_file(aptdir, distro, pkg):
return subprocess.check_output(['apt-file',
'-s', '%s/%s.list' % (aptdir, distro),
'-c', '%s/apt-file/%s' % (aptdir, distro),
'-l', '-a', 'i386',
'-l', '-i', '-a', 'amd64',
'search', pkg]).decode('utf8')
class Apt:
@ -62,19 +62,22 @@ class Apt:
if self.aptdir:
self.distros = sorted([x[:-5] for x in os.listdir(self.aptdir) if x.endswith('.list')])
def find(self, pkg, chkdistro, filelookup=True):
_pkg = ''.join([x for x in pkg.strip().split(None,1)[0] if x.isalnum() or x in '.-_+/'])
distro = ''
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 not distro:
distro = chkdistro
def _parse(self, pkg):
parser = FeedParser()
parser.feed(pkg)
return parser.close()
def find(self, pkg, distro, filelookup=True):
if distro not in self.distros:
return "%s is not a valid distribution: %s" % (distro, ", ".join(self.distros))
pkg = _pkg
return "%r is not a valid release: %s" % (distro, ", ".join(self.distros))
if distro.split('-')[0] in ('oldstable', 'stable', 'unstable', 'testing', 'experimental'):
pkgTracURL = "https://packages.debian.org"
else:
pkgTracURL = "https://packages.ubuntu.com"
try:
data = apt_cache(self.aptdir, distro, ['search', '-n', pkg])
data = apt_cache(self.aptdir, distro, ['search', '-n'], pkg)
except subprocess.CalledProcessError as e:
data = e.output
if not data:
@ -85,111 +88,81 @@ class Apt:
data = e.output
if data:
if data[0] == 'sh:': # apt-file isn't installed
self.log.error("PackageInfo/packages: apt-file is not installed")
return "Please use http://packages.ubuntu.com/ to search for files"
self.log.error("PackageInfo/packages: apt-file is not installed")
return "Please use %s/ to search for files" % pkgTracURL
if data[0] == 'E:': # No files in the cache dir
self.log.error("PackageInfo/packages: Please run the 'update_apt_file' script")
return "Cache out of date, please contact the administrator"
self.log.error("PackageInfo/packages: Please run the 'update_apt_file' script")
return "Cache out of date, please contact the administrator"
if data[0] == "Use" and data[1] == "of":
url = "http://packages.ubuntu.com/search?searchon=contents&keywords=%s&mode=&suite=%s&arch=any" % (utils.web.urlquote(pkg), distro)
return url
return "%s/search?searchon=contents&keywords=%s&mode=exactfilename&suite=%s&arch=any" % (pkgTracURL, utils.web.urlquote(pkg), distro)
if len(data) > 10:
return "File %s found in %s (and %d others) http://packages.ubuntu.com/search?searchon=contents&keywords=%s&mode=&suite=%s&arch=any" % (pkg, ', '.join(data[:10]), len(data)-10, utils.web.urlquote(pkg), distro)
return "File %s found in %s and %d others <%s/search?searchon=contents&keywords=%s&mode=exactfilename&suite=%s&arch=any>" % (pkg, ', '.join(data[:10]), len(data)-10, pkgTracURL, utils.web.urlquote(pkg), distro)
return "File %s found in %s" % (pkg, ', '.join(data))
return 'Package/file %s does not exist in %s' % (pkg, distro)
return "No packages matching '%s' could be found" % pkg
pkgs = [x.split()[0] for x in data.split('\n') if x]
if len(pkgs) > 10:
return "Found: %s (and %d others) http://packages.ubuntu.com/search?keywords=%s&searchon=names&suite=%s&section=all" % (', '.join(pkgs[:10]), len(pkgs)-10, utils.web.urlquote(pkg), distro)
return "Found: %s and %d others <%s/search?keywords=%s&searchon=names&suite=%s&section=all>" % (', '.join(pkgs[:10]), len(pkgs)-10, pkgTracURL, utils.web.urlquote(pkg), distro)
else:
return "Found: %s" % ', '.join(pkgs[:5])
def raw_info(self, pkg, chkdistro):
if not pkg.strip():
return ''
_pkg = ''.join([x for x in pkg.strip().split(None,1)[0] if x.isalnum() or x in '.-_+'])
distro = chkdistro
if len(pkg.strip().split()) > 1:
distro = ''.join([x for x in pkg.strip().split(None,2)[1] if x.isalnum() or x in '-._+'])
def raw_info(self, pkg, distro, archlookup=True):
if distro not in self.distros:
return "%r is not a valid distribution: %s" % (distro, ", ".join(self.distros))
pkg = _pkg
return "%r is not a valid release: %s" % (distro, ", ".join(self.distros))
try:
data = apt_cache(self.aptdir, distro, ['show', pkg])
data = apt_cache(self.aptdir, distro, ['show'], pkg)
except subprocess.CalledProcessError as e:
data = e.output
try:
data2 = apt_cache(self.aptdir, distro, ['showsrc', pkg])
except subprocess.CalledProcessError as e:
data2 = e.output
if not data or 'E: No packages found' in data:
return 'Package %s does not exist in %s' % (pkg, distro)
maxp = {'Version': '0~'}
packages = [x.strip() for x in data.split('\n\n')]
packages = list(map(self._parse, [x for x in data.split('\n\n') if x]))
for p in packages:
if not p.strip():
continue
parser = FeedParser()
parser.feed(p)
p = parser.close()
if type(p) == type(""):
self.log.error("PackageInfo/packages: apt returned an error, do you have the deb-src URLs in %s.list?" % distro)
return "Package lookup faild"
if not p.get("Version", None):
continue
if apt.apt_pkg.version_compare(maxp['Version'], p['Version']) <= 0:
maxp = p
del parser
if not archlookup:
return maxp
try:
data2 = apt_cache(self.aptdir, distro, ['showsrc'], pkg)
except subprocess.CalledProcessError:
return maxp
maxp2 = {'Version': '0~'}
packages2 = [x.strip() for x in data2.split('\n\n')]
packages2 = list(map(self._parse, [x for x in data2.split('\n\n') if x]))
for p in packages2:
if not p.strip():
continue
parser = FeedParser()
parser.feed(p)
p = parser.close()
if type(p) == type(""):
self.log.error("PackageInfo/packages: apt returned an error, do you have the deb-src URLs in %s.list?" % distro)
return "Package lookup faild"
if not p['Version']:
continue
if apt.apt_pkg.version_compare(maxp2['Version'], p['Version']) <= 0:
maxp2 = p
del parser
archs = ''
if 'Architecture' in maxp2:
archs = [_.strip() for _ in maxp2['Architecture'].split() if _.strip()]
for arch in archs:
if arch not in ('any', 'all'):
continue
else:
archs = ''
break
if archs:
archs = ' (Only available for %s)' % '; '.join(archs)
archs = re.match(r'.*^ %s \S+ \S+ \S+ arch=(?P<arch>\S+)$' % re.escape(pkg), maxp2['Package-List'],
re.I | re.M | re.DOTALL)
if archs:
archs = archs.group('arch').split(',')
if not ('any' in archs or 'all' in archs):
maxp['Architectures'] = ', '.join(archs)
maxp["Distribution"] = distro
maxp["Architectures"] = archs
return maxp
def info(self, pkg, chkdistro):
maxp = self.raw_info(pkg, chkdistro)
def info(self, pkg, distro):
maxp = self.raw_info(pkg, distro)
if isinstance(maxp, str):
return maxp
return("%s (source: %s): %s. In component %s, is %s. Version %s (%s), package size %s kB, installed size %s kB%s" %
(maxp['Package'], maxp['Source'] or maxp['Package'], description(maxp), component(maxp['Section']),
maxp['Priority'], maxp['Version'], maxp["Distribution"], int(maxp['Size'])/1024, maxp['Installed-Size'], maxp["Architectures"]))
(maxp['Package'], maxp.get('Source', None) or maxp['Package'], description(maxp), component(maxp['Section']),
maxp['Priority'], maxp['Version'], distro, int(maxp['Size'])/1024, maxp['Installed-Size'],
". (Only available for %s)" % maxp['Architectures'] if maxp.get('Architectures', None) else ""))
def depends(self, pkg, chkdistro):
maxp = self.raw_info(pkg, chkdistro)
def depends(self, pkg, distro):
maxp = self.raw_info(pkg, distro, archlookup=False)
if isinstance(maxp, str):
return maxp
return("%s (version %s in %s) depends on: %s" %
(maxp['Package'], maxp["Version"], maxp["Distribution"], maxp["Depends"]))
return("%s (version: %s, %s): Depends on %s%s" %
(maxp['Package'], maxp['Version'], distro, maxp.get('Depends', None) or "nothing",
". Recommends %s" % maxp['Recommends'] if maxp.get('Recommends', None) else ""))
# Simple test
if __name__ == "__main__":
import sys
@ -210,21 +183,14 @@ if __name__ == "__main__":
def registryValue(self, *args, **kwargs):
return "/home/bot/aptdir"
command = argv[1].split(None, 1)[0]
try:
lookup = argv[1].split(None, 1)[1]
(command, lookup) = argv[1].split(None, 1)
except:
print("Need something to lookup")
print("Need something to look up")
sys.exit(1)
dists = "hardy"
dist = "zesty"
if argc == 3:
dists = argv[2]
dist = argv[2]
plugin = FakePlugin()
aptlookup = Apt(plugin)
if command == "find":
print(aptlookup.find(lookup, dists))
elif command == "depends":
print(aptlookup.depends(lookup, dists))
else:
print(aptlookup.info(lookup, dists))
print(getattr(aptlookup, command)(lookup, dist))

View File

@ -14,69 +14,79 @@
#
###
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.utils as utils
import supybot.ircutils as ircutils
import supybot.ircmsgs as ircmsgs
import supybot.callbacks as callbacks
import supybot.ircutils as ircutils
import supybot.ircdb as ircdb
import supybot.callbacks as callbacks
import supybot.conf as conf
import os
import re
import time
import re, time
from . import packages
def get_user(msg):
try:
user = ircdb.users.getUser(msg.prefix)
except:
return False
return user
_stripNickChars = """!"#$%&'()*+,./:;<=>?@~"""
def stripNick(nick):
while nick and nick[-1] in _stripNickChars:
nick = nick[:-1]
return nick
def defaultIgnored(hostmask, recipient):
if not conf.supybot.defaultIgnore():
return False
if conf.version <= '0.83.4.1' \
and ircutils.isChannel(recipient):
return False
try:
user = ircdb.users.getUser(hostmask)
except KeyError:
return True
return False
def checkIgnored(hostmask, recipient):
try:
user = ircdb.users.getUser(hostmask)
if user._checkCapability('owner'):
return False
elif user.ignore:
return True
except KeyError:
pass
if ircdb.ignores.checkIgnored(hostmask):
return True
if ircutils.isChannel(recipient):
c = ircdb.channels.getChannel(recipient)
if c.checkIgnored(hostmask):
return True
return False
## Taken from Encyclopedia ##
# Repeat filtering message queue
msgcache = {}
def queue(irc, to, msg):
def queue(irc, target, msg):
now = time.time()
for m in list(msgcache.keys()):
if msgcache[m] < now - 30:
msgcache.pop(m)
for m in msgcache:
if m[0] == irc and m[1] == to:
if m[0] == irc and m[1] == target:
oldmsg = m[2]
if msg == oldmsg or oldmsg.endswith(msg):
if oldmsg.endswith(msg):
break
if msg.endswith(oldmsg):
msg = msg[:-len(oldmsg)] + 'please see above'
if msg.endswith(oldmsg) and msg[:-len(oldmsg)].endswith(': '):
msg = msg[:-len(oldmsg)] + 'Please see above'
else:
msgcache[(irc, to, msg)] = now
irc.queueMsg(ircmsgs.privmsg(to, msg))
msgcache[(irc, target, msg)] = now
irc.reply(msg, to=target)
class PackageInfo(callbacks.Plugin):
"""Lookup package information via apt-cache/apt-file"""
"""Look up package information via apt-cache/apt-file"""
threaded = True
space_re = re.compile(r' *')
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:
release = release.strip()
@ -87,253 +97,124 @@ class PackageInfo(callbacks.Plugin):
return (None, None)
if not release:
return (defaultRelease, None)
(release, rest) = (release.split(' ', 1) + [None])[:2]
if release[0] in ('|', '>'):
(release, rest) = (release.split(None, 1) + [None])[:2]
if release[0] in '|>':
return (defaultRelease, "%s %s" % (release, rest))
return (release, rest)
def __getChannel(self, channel):
return ircutils.isChannel(channel) and channel or None
def __handleRest(self, msg, target, reply, rest):
targeto = target
prefix = ''
if rest[0] == '|':
rest = rest.lstrip('|').strip()
if rest:
if rest.lower() == "me":
rest = msg.nick
prefix = "%s: " % rest
elif rest[0] == '>':
rest = rest.lstrip('>').strip()
if rest:
# Take the first "nick" and strip off bad chars
target = stripNick(rest.split()[0])
if target.lower() == "me":
target = msg.nick
prefix = "<%s> wants you to know: " % msg.nick
def __getReplyChars(self, channel):
prefix_chars = list(self.registryValue("prefixchar", channel))
address_chars = list(str( conf.supybot.reply.whenAddressedBy.chars() ))
if channel:
address_chars = list(str( conf.supybot.reply.whenAddressedBy.chars.get(channel) ))
return tuple(set(prefix_chars + address_chars))
if target.lower() != targeto.lower() and ircutils.isChannel(target):
target = targeto
prefix = "(Forwarding to channels is not permitted) "
elif msg.nick.lower() in (target.lower(), prefix[:-2].lower()) \
and msg.nick.lower() != targeto.lower():
target = msg.nick
prefix = "(In the future, please use a private message to investigate) "
def __getCommand(self, text, channel):
reply_chars = self.__getReplyChars(channel)
my_commands = self.listCommands()
if text[0] in reply_chars:
text = text[1:]
return text.strip().lower().split(' ', 1)[0]
return (target, prefix + reply)
def real_info(self, irc, msg, args, package, release):
def real_info(self, irc, msg, args, package, release=None):
"""<package> [<release>]
Lookup information for <package>, optionally in <release>
Look up information for <package>, optionally in <release>
"""
channel = self.__getChannel(msg.args[0])
reply_target = ircutils.replyTo(msg)
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if not self.registryValue("enabled", channel):
return
(release, rest) = self.__getRelease(irc, release, channel)
if not release:
return
target = ircutils.replyTo(msg)
reply = self.Apt.info(package, release)
if rest:
if rest[0] == '|':
try:
target = rest
while target[0] == '|':
target = target[1:].strip()
if target.lower() == "me":
target = msg.nick
queue(irc, reply_target, "%s: %s" % (target, reply))
return
except Exception as e:
self.log.info("PackageInfo: (info) Exception in pipe: %r" % e)
pass
elif rest[0] == '>':
try:
while rest[0] == '>':
rest = rest[1:].strip()
targets = [_ for _ in rest.split() if _] # Split and discard empty parts
target = stripNick(targets[0]) # Take the first "nick" and strip off bad chars
if target.lower() == "me":
target = msg.nick # redirect
if not target: # Throw error
raise Exception('No target')
queue(irc, target, "<%s> wants you to know: %s" % (msg.nick, reply))
return
except Exception as e:
self.log.info("PackageInfo: (info) Exception in redirect: %r" % e)
pass
queue(irc, reply_target, reply)
(target, reply) = self.__handleRest(msg, target, reply, rest)
queue(irc, target, reply)
info = wrap(real_info, ['anything', optional('text')])
def real_depends(self, irc, msg, args, package, release):
def real_depends(self, irc, msg, args, package, release=None):
"""<package> [<release>]
Lookup dependencies for <package>, optionally in <release>
Look up dependencies for <package>, optionally in <release>
"""
channel = self.__getChannel(msg.args[0])
reply_target = ircutils.replyTo(msg)
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if not self.registryValue("enabled", channel):
return
(release, rest) = self.__getRelease(irc, release, channel)
if not release:
return
target = ircutils.replyTo(msg)
reply = self.Apt.depends(package, release)
if rest:
if rest[0] == '|':
try:
target = rest
while target[0] == '|':
target = target[1:].strip()
if target.lower() == "me":
target = msg.nick
queue(irc, reply_target, "%s: %s" % (target, reply))
return
except Exception as e:
self.log.info("PackageInfo: (depends) Exception in pipe: %r" % e)
pass
elif rest[0] == '>':
try:
while rest[0] == '>':
rest = rest[1:].strip()
targets = [_ for _ in rest.split() if _] # Split and discard empty parts
target = stripNick(targets[0]) # Take the first "nick" and strip off bad chars
if target.lower() == "me":
target = msg.nick # redirect
if not target: # Throw error
raise Exception('No target')
queue(irc, target, "<%s> wants you to know: %s" % (msg.nick, reply))
return
except Exception as e:
self.log.info("PackageInfo: (depends) Exception in redirect: %r" % e)
pass
queue(irc, reply_target, reply)
(target, reply) = self.__handleRest(msg, target, reply, rest)
queue(irc, target, reply)
depends = wrap(real_depends, ['anything', optional('text')])
def real_find(self, irc, msg, args, package, release):
def real_find(self, irc, msg, args, package, release=None):
"""<package/filename> [<release>]
Search for <package> or, of that fails, find <filename>'s package(s).
Optionally in <release>
Search for <package> or, if that fails, <filename> in packages,
optionally in <release>
"""
channel = self.__getChannel(msg.args[0])
reply_target = ircutils.replyTo(msg)
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if not self.registryValue("enabled", channel):
return
(release, rest) = self.__getRelease(irc, release, channel)
if not release:
return
target = ircutils.replyTo(msg)
reply = self.Apt.find(package, release)
if rest:
if rest[0] == '|':
try:
target = rest
while target[0] == '|':
target = target[1:].strip()
if target.lower() == "me":
target = msg.nick
queue(irc, reply_target, "%s: %s" % (target, reply))
return
except Exception as e:
self.log.info("PackageInfo: (find) Exception in pipe: %r" % e)
pass
elif rest[0] == '>':
try:
while rest[0] == '>':
rest = rest[1:].strip()
targets = [_ for _ in rest.split() if _] # Split and discard empty parts
target = stripNick(targets[0]) # Take the first "nick" and strip off bad chars
if target.lower() == "me":
target = msg.nick # redirect
if not target: # Throw error
raise Exception('No target')
queue(irc, target, "<%s> wants you to know: %s" % (msg.nick, reply))
return
except Exception as e:
self.log.info("PackageInfo: (find) Exception in redirect: %r" % e)
pass
queue(irc, reply_target, reply)
(target, reply) = self.__handleRest(msg, target, reply, rest)
queue(irc, target, reply)
find = wrap(real_find, ['anything', optional('text')])
def privmsg(self, irc, msg, user):
channel = self.__getChannel(msg.args[0])
text = self.space_re.subn(' ', msg.args[1].strip())[0]
my_commands = self.listCommands()
if text[0] == self.registryValue("prefixchar"):
text = text[1:].strip()
if user and text[0] in list(conf.supybot.reply.whenAddressedBy.chars()):
return
(cmd, rest) = (text.split(' ', 1) + [None])[:2]
if cmd not in my_commands:
return
if not rest:
return
(term, rest) = (rest.split(' ', 1) + [None])[:2]
if cmd == "find":
self.real_find(irc, msg, [], term, rest)
elif cmd == "depends":
self.real_depends(irc, msg, [], term, rest)
else:
self.real_info(irc, msg, [], term, rest)
def chanmsg(self, irc, msg, user):
channel = self.__getChannel(msg.args[0])
text = self.space_re.subn(' ', msg.args[1].strip())[0]
my_commands = self.listCommands()
if text[0] != self.registryValue("prefixchar", channel):
return
text = text[1:]
(cmd, rest) = (text.split(' ', 1) + [None])[:2]
if cmd not in my_commands:
return
if not rest:
return
(term, rest) = (rest.split(' ', 1) + [None])[:2]
if cmd == "find":
self.real_find(irc, msg, [], term, rest)
elif cmd == "depends":
self.real_depends(irc, msg, [], term, rest)
else:
self.real_info(irc, msg, [], term, rest)
def doPrivmsg(self, irc, msg):
if chr(1) in msg.args[1]: # CTCP
return
if not msg.args[1]:
text = msg.args[1].strip()
if not text:
return
channel = self.__getChannel(msg.args[0])
if not self.registryValue("enabled", channel):
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if text[0] == self.registryValue("prefixchar", channel):
text = text[1:].strip()
elif channel or text[0] in conf.supybot.reply.whenAddressedBy.chars():
return
user = get_user(msg)
if channel:
self.chanmsg(irc, msg, user)
else:
if user:
return
self.privmsg(irc, msg, user)
if not text:
return
(cmd, rest) = (text.split(None, 1) + [None])[:2]
if not cmd:
return
cmd = cmd.lower()
if not (cmd in ("info", "depends", "find") and rest):
return
(package, release) = (rest.split(None, 1) + [None])[:2]
irc = callbacks.NestedCommandsIrcProxy(irc, msg, [])
getattr(self, "real_%s" % cmd)(irc, msg, [], package, release)
def inFilter(self, irc, msg):
if msg.command != "PRIVMSG":
if not defaultIgnored(msg.prefix, msg.args[0]):
return msg
if not conf.supybot.defaultIgnore():
if checkIgnored(msg.prefix, msg.args[0]):
return msg
text = msg.args[1].strip()
if len(text) < 6:
return msg
user = get_user(msg)
channel = self.__getChannel(msg.args[0])
reply_chars = self.__getReplyChars(channel)
my_commands = self.listCommands()
cmd = self.__getCommand(text, channel)
if cmd not in my_commands:
return msg
if user:
if not channel and text[0] == self.registryValue("prefixchar"):
msg.args = (msg.args[0], text[1:])
return msg
if channel:
if text[0] not in reply_chars:
return msg
# if not hasattr(irc, 'reply'):
# irc = callbacks.ReplyIrcProxy(irc, msg)
# self.doPrivmsg(irc, msg)
else:
if text[1] in reply_chars:
msg.args = (msg.args[0], text[1:])
irc = callbacks.ReplyIrcProxy(irc, msg)
if msg.command == "PRIVMSG":
self.doPrivmsg(irc, msg)
return msg
Class = PackageInfo

View File

@ -7,76 +7,65 @@ DIR=""
# Be quiet by default.
VERBOSE=0
## Please don't change anything below this line, unless you really know what
## you are doing and don't bother me with whatever errors it produces :)
# Print usage information.
usage() {
echo "Usage $0 [OPTION]..."
echo "Updates the APT package cache for PackageInfo"
echo ""
echo "-h, --help Display this message and exit."
echo "-v, --verbose Be more verbose than normal."
echo "-V, --very-verbose Be even more verbose than normal."
echo "-d, --dir[=DIR] Sets the directory to use when updating the APT package cache."
echo ""
echo "Note:"
echo " Please separate each option with a space, eg:"
echo " $0 -v -d /home/bot/aptdir"
echo " Rather than:"
echo " $0 -vd /home/bot/aptdir"
echo ""
echo "This script is intended to be ran automatically (eg: cron), so it shows no output by default."
echo "You can make the script more verbose with either the -v/--verbose or -V/--very-verbose options."
echo "The -d/--dir option sets the directory where this script looks for *.list files for apt-get."
cat <<EOF
Usage: $0 [OPTION]...
Updates the APT package cache for PackageInfo.
-d, --dir <DIR> Specify directory for APT package cache.
-v, --verbose Be more verbose than normal.
-V, --very-verbose Be even more verbose than normal.
-h, --help Display this message and exit.
Note:
Please separate each option with a space, eg:
$0 -v -d /home/bot/aptdir
Rather than:
$0 -vd /home/bot/aptdir
This script is intended to be run automatically (eg: cron), so it shows no output by default.
You can make the script more verbose with the -v/--verbose or -V/--very-verbose options.
The -d/--dir option sets the directory where this script looks for *.list files for apt-get.
EOF
}
# Prints an error message, usage (above), then exit with the specified exit value.
# Prints an error message, usage (above), then exits with the specified exit value.
error() {
local exit_val=$1
shift
echo $@ >&2
echo "$@" >&2
usage >&2
exit $exit_val
}
# Runs apt-get update in the specified directory for the specified distribution.
update_apt() {
local apt_dir="$1"
local dist="$2"
local apt_args=""
if [ $VERBOSE -eq 0 ]; then
apt_args="-qq"
elif [ $VERBOSE -eq 1 ]; then
apt_args="-q"
fi
apt-get $apt_args -o="APT::Architecture=i386" \
apt-get $apt_args \
-o="APT::Architecture=amd64" \
-o="APT::Architectures::=i386" \
-o "APT::Architectures::=amd64" \
-o="Dir::State::Lists=$apt_dir/$dist" \
-o="Dir::State::Status=$apt_dir/$dist.status" \
-o="Dir::Cache=$apt_dir/cache" \
-o="Dir::Etc::SourceList=$apt_dir/$dist.list" \
-o="Dir::Etc::SourceParts=\"\"" \
-o="APT::Architectures::=amd64" \
-o="Dir::State::Lists=$DIR/$DIST" \
-o="Dir::State::Status=$DIR/$DIST.status" \
-o="Dir::Etc::SourceList=$DIR/$DIST.list" \
-o="Dir::Etc::SourceParts=''" \
-o="Dir::Cache=$DIR/cache" \
update
return $?
}
# main()
# Acepted arguments are:
# -h,--help
# -v,--verbose
# -V,--very-verbose
# -d,--dir[=DIR]
# Check command-line arguments
while [ $# -gt 0 ]; do
case "$1" in
case $1 in
-h|--help)
usage
exit 0
@ -88,13 +77,13 @@ while [ $# -gt 0 ]; do
VERBOSE=2
;;
-d|--dir)
[ -z "$2" ] && error 1 "\"-d|--dir\" requires an argument."
[ -z "$2" ] && error 1 '"-d/--dir" requires an argument.'
shift
DIR="$1"
;;
--dir=*)
DIR="${1:6}"
[ -z "$DIR" ] && error 1 "\"--dir\" requires an argument."
[ -z "$DIR" ] && error 1 '"--dir" requires an argument.'
;;
-*)
error 1 "Unknown option \"$1\"."
@ -106,38 +95,33 @@ while [ $# -gt 0 ]; do
shift
done
apt_get=$(which apt-get 2>/dev/null)
# Check that apt-get exists and bail if it doesn't.
if [ $? -ne 0 ]; then
echo "ERROR: apt-get not found. Please install apt-get in your \$PATH." >&2
# Check if apt-get is installed and bail if not.
if ! which apt-get >/dev/null 2>&1; then
echo "ERROR: apt-get not found. Please install apt-get in your PATH." >&2
exit 1
fi
#TODO: Remove this section and error out if DIR is not set,
# This could hide errors where DIR/-d was not set, an error message.
if [ -z "$DIR" ]; then
DIR=/home/bot/aptdir
echo "WARNING: No DIR set and no -d/--dir option given, defaulting to \"$DIR\"" >&2
echo "WARNING: Please set DIR on line 5 of $(readlink -f $0) or use the -d/--dir option" >&2
error 1 "ERROR: No DIR set in the script and no -d/--dir option given."
fi
#[ -z "$DIR" ] && error 1 "ERROT: Please set DIR on line 5 of $(readlink -f $0) or use the -d/--dir option"
items=("$DIR"/*.list)
[ ! -e "${items[0]}" ] && error 1 "ERROR: Could not find any *.list files in \"$DIR\"."
DIR="$(echo $DIR | sed 's,/*$,,')" # Normalize $DIR
items=$(ls "${DIR}"/*.list 2>/dev/null)
[ $? -ne 0 ] && error 1 "Could not find \"*.list\" files in \"$DIR\"."
for DIST in $items; do
for DIST in "${items[@]}"; do
[ -h "$DIST" ] && continue # Ignore symbolic links
# Extract the distribution name from the .list file name.
DIST="${DIST:${#DIR}}"
DIST="${DIST/.list}"
DIST="${DIST:1}"
DIST=${DIST##*/}
DIST=${DIST%.list}
touch "${DIR}/${DIST}.status" # Create APT status file
mkdir -p "${DIR}/${DIST}/partial" # APT needs this to exist
update_apt "$DIR" "$DIST" # Update the package list with apt-get
[ $VERBOSE -gt 0 ] && echo "INFO: Processing $DIST"
touch "$DIR/$DIST.status" # Create APT status file
mkdir -p "$DIR/$DIST/partial" # APT needs this to exist
update_apt # Update APT package cache
if [ $? -ne 0 ]; then
[ $VERBOSE -eq 0 ] && echo "Try passing -v or -V to get the error message." >&2
echo "ERROR: apt-get update failed for $DIST." >&2
exit_val=$?
fi
done
exit $exit_val

View File

@ -4,7 +4,7 @@
# or use the --dir command-line option.
DIR=""
# Be quiet by default
# Be quiet by default.
VERBOSE=0
## Please don't change anything below this line, unless you really know what
@ -12,53 +12,48 @@ VERBOSE=0
# Print usage information.
usage() {
echo "Usage $0 [OPTION]..."
echo "Updates the apt-file cache for PackageInfo"
echo ""
echo "-h, --help Display this message and exit."
echo "-v, --verbose Be more verbose than normal."
echo "-d, --dir[=DIR] Sets the directory to use when updating the apt-file cache."
echo ""
echo "Note:"
echo " Please separate each option with a space, eg:"
echo " $0 -v -d /home/bot/aptdir"
echo " Rather than:"
echo " $0 -vd /home/bot/aptdir"
echo ""
echo "This script is intended to be ran automatically (eg: cron), so it shows no output by default."
echo "You can make the script more verbose with the -v/--verbose option."
echo "The -d/--dir option sets the directory where this script looks for *.list files for apt-file."
cat <<EOF
Usage: $0 [OPTION]...
Updates the apt-file cache for PackageInfo.
-d, --dir <DIR> Specify directory for apt-file cache.
-v, --verbose Be more verbose than normal.
-h, --help Display this message and exit.
Note:
Please separate each option with a space, eg:
$0 -v -d /home/bot/aptdir
Rather than:
$0 -vd /home/bot/aptdir
This script is intended to be run automatically (eg: cron), so it shows no output by default.
You can make the script more verbose with the -v/--verbose option.
The -d/--dir option sets the directory where this script looks for *.list files for apt-file.
EOF
}
# Prints an error message, usage (above), then exit with the specified exit value.
# Prints an error message, usage (above), then exits with the specified exit value.
error() {
local exit_val=$1
shift
echo $@ >&2
echo "$@" >&2
usage >&2
exit $exit_val
}
# Runs apt-file in the specified directory against the specified distribution.
update_apt() {
local DIST="$1"
# Runs apt-file update in the specified directory for the specified distribution.
update_apt_file() {
if [ $VERBOSE -eq 0 ]; then
apt-file -N -l -c "$DIR/apt-file/$DIST" -s "$DIR/$DIST.list" -a i386 update >/dev/null 2>&1
apt-file -N -l -c "$DIR/apt-file/$DIST" -s "$DIR/$DIST.list" -a amd64 update >/dev/null 2>&1
else
apt-file -N -l -c "$DIR/apt-file/$DIST" -s "$DIR/$DIST.list" -a i386 update
apt-file -N -l -c "$DIR/apt-file/$DIST" -s "$DIR/$DIST.list" -a amd64 update
fi
}
# main()
# Acepted arguments are:
# -h,--help
# -v,--verbose
# -d,--dir[=DIR]
# Check command-line arguments
while [ $# -ne 0 ]; do
case "$1" in
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
usage
exit 0
@ -67,61 +62,50 @@ while [ $# -ne 0 ]; do
VERBOSE=1
;;
-d|--dir)
[ -z "$2" ] && error 1 "\"-d|--dir\" requires an argument."
[ -z "$2" ] && error 1 '"-d/--dir" requires an argument.'
shift
DIR="$1"
;;
--dir=*)
DIR="${1:6}"
[ -z "$DIR" ] && error 1 "\"--dir\" requires an argument."
[ -z "$DIR" ] && error 1 '"--dir" requires an argument.'
;;
-*)
error 1 "Unknown option \"$1\"."
;;
*)
error 1 "This script takes no non-argument parameterss."
error 1 "This script takes no non-argument parameters."
;;
esac
shift
done
apt_file=$(which apt-file 2>/dev/null)
# Check if apt-file is installed and bail if it isn't.
if [ $? -ne 0 ]; then
echo "ERROR: apt-file not found. Please install apt-file in your \$PATH." >&2
# Check if apt-file is installed and bail if not.
if ! which apt-file >/dev/null 2>&1; then
echo "ERROR: apt-file not found. Please install apt-file in your PATH." >&2
exit 1
fi
#TODO: Remove this and error out if DIR is not set,
# This is legacy code and needs to disappear sometime.
if [ -z "$DIR" ]; then
DIR=/home/bot/aptdir
echo "WARNING: No DIR set and no -d/--dir option given, defaulting to \"$DIR\"" >&2
echo "WARNING: Please set DIR on line 5 of $(readlink -f $0) or use the -d/--dir option" >&2
error 1 "ERROR: No DIR set in the script and no -d/--dir option given."
fi
#[ -z "$DIR" ] && error 1 "Please set DIR on line 5 of $(readlink -f $0) or use the -d/--dir option"
items=("$DIR"/*.list)
[ ! -e "${items[0]}" ] && error 1 "ERROR: Could not find any *.list files in \"$DIR\"."
DIR="$(echo $DIR | sed 's,/*$,,')" # Normalize $DIR
for DIST in "${items[@]}"; do
[ -h "$DIST" ] && continue # Ignore symbolic links
# Extract the distribution name from the .list file name.
DIST=${DIST##*/}
DIST=${DIST%.list}
items=$(ls "${DIR}"/*.list 2>/dev/null)
[ $? -ne 0 ] && error 1 "ERROR: Could not find \"*.list\" files in \"$DIR\"."
for DIST in $items; do
[ -h $DIST ] && continue # Ignore symbolic links
# Extract the distribution from the .list file name
DIST="${DIST:${#DIR}}"
DIST="${DIST/.list}"
DIST="${DIST:1}"
mkdir -p "$DIR/apt-file/$DIST" # Create apt-file directory, if it doesn't exist
[ $VERBOSE -ne 0 ] && echo "INFO: Processing $DIST"
update_apt "$DIST" # Update apt-file database
[ $VERBOSE -gt 0 ] && echo "INFO: Processing $DIST"
mkdir -p "$DIR/apt-file/$DIST" # Create apt-file directory if it doesn't exist
update_apt_file # Update apt-file cache
if [ $? -ne 0 ]; then
[ $VERBOSE -eq 0 ] && echo "Try passing -v to get the error message." >&2
error 1 "ERROR: apt-file failed for ${DIST}!."
echo "ERROR: apt-file update failed for $DIST." >&2
exit_val=$?
fi
done
exit $exit_val