From 15128b94b496324a1c0afd6cbdd7fffbdaa18ba7 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Thu, 17 Jun 2021 01:58:33 -0400 Subject: Initial commit. --- SlackTools/plugin.py | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 SlackTools/plugin.py (limited to 'SlackTools/plugin.py') diff --git a/SlackTools/plugin.py b/SlackTools/plugin.py new file mode 100644 index 0000000..e4cc8bb --- /dev/null +++ b/SlackTools/plugin.py @@ -0,0 +1,202 @@ +### +# Copyright (c) 2021, B. Watson +# All rights reserved. +# +# +### + +import os +import subprocess +import sys +import re +import sqlite3 + +from supybot import utils, plugins, ircutils, callbacks +from supybot.commands import * +import supybot.utils.minisix as minisix + +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('SlackTools') +except ImportError: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + +# This code could stand to be refactored. Lots of almost-identical +# code in FileQuery and PkgQuery. Also, I'm pretty much a python +# n00b, so the code might read a little weird if you're a python +# expert (I learned just enough of the language, on the fly, to +# get this stuff written). That's also why the database-creation +# script is in perl (I'm much more familiar and competent with perl). + +# notes about the database: + +# it gets created by the makepkgdb.pl script, which requires: +# - PACKAGES.txt and extra/PACKAGES.txt from the installer. +# - copies of /var/log/scripts and /var/log/packages from a full +# slackware install (including /extra but not /testing nor +# /pasture) with no 3rd-party packages installed. +# see the comments in makepkgdb.pl for more info. + +# The database only contains packages from one slackware version +# and arch. Currently that's x86_64 14.2. Only packages from the +# main slackware64/ and extra/ trees are in the db. If there's any +# demand for it, it wouldn't be hard to support multiple slack +# versions and/or arches... or add pasture/ and/or testing/ to the db. + +# It would have been easy to include package sizes in the db, but +# I didn't think it would be that useful. + +# The files table also includes symlinks. + +# /lib64/incoming/ paths get their /incoming removed before being +# inserted in the db. + +# file paths are stored *with* the leading slash, so e.g. /bin/ls +# rather than the bin/ls. This is unlike usual slackpkg and +# /var/log/packages usage, but it seems more intuitive for users. + + +class SlackTools(callbacks.Plugin): + """Provides Slackware package db lookups""" + threaded = True + + db = None + + def getMaxResults(self, msg): + maxresults = self.registryValue('maxresults') + if msg.channel is None: + # private message, increase limit + maxresults *= 5 + return maxresults + + def InitDB(self): + if self.db is None: + filename = self.registryValue('dbpath') + if(os.path.exists(filename)): + self.db = sqlite3.connect(filename) + return self.db + + def PkgQuery(self, irc, msg, searchtype, term): + db = self.InitDB(); + if db is None: + irc.error("slackpkg database doesn't exist", Raise=True) + + maxresults = self.getMaxResults(msg) + cursor = db.cursor() + if(searchtype == 'pkg'): + cursor.execute("select c.name, p.name, p.descrip from categories c, packages p where c.id=p.category and p.name glob ? order by c.name, p.name limit ?", (term, maxresults+1)) + else: + cursor.execute("select distinct c.name, p.name, p.descrip from categories c, packages p, files f where c.id=p.category and p.id=f.package and f.path glob ? order by c.name, p.name limit ?", (term, maxresults+1)) + + result = cursor.fetchall() + + lines = [] + + if len(result) == 0: + irc.reply("no matching packages") + else: + for (category, pkg, descrip) in result: + lines.append(ircutils.bold(category + "/" + pkg) + ": " + descrip) + if(len(result) > maxresults): + lines.append("[too many results, only showing first " + str(maxresults) + "]") + irc.replies(lines, joiner=' | ') + + + # search for packages by name + def pkgsearch(self, irc, msg, args, pkg): + """ + + Search the Slackware package database for packages named like . + This is a case-sensitive substring match (e.g. "core" matches "coreutils" + and "xapian-core"). You can use * for globbing, e.g. "c*s" to find all + packages whose names begin with "c" and end with "s". + """ + + self.PkgQuery(irc, msg, 'pkg', "*" + pkg + "*") + + pkgsearch = thread(wrap(pkgsearch, ['somethingWithoutSpaces'])) + + # search for packages by contents + def filesearch(self, irc, msg, args, file): + """ + + Search the Slackware package database for packages that contain files + named . This is a case-sensitive exact match, by default (e.g. + "/bin/ls" does not match "/usr/bin/ls" nor "/bin/lsattr"). You can use * + for globbing, e.g. "/bin/ls*" to match all files in /bin whose names begin + with "ls". + """ + + self.PkgQuery(irc, msg, 'file', file) + + filesearch = thread(wrap(filesearch, ['somethingWithoutSpaces'])) + + # run filesearch or pkgsearch, save some typing. + def pkg(self, irc, msg, args, fileflag, term): + """ [-f] + + Without -f, same as pkgsearch. With -f, same as filesearch. + See the help for pkgsearch and/or filesearch for details. + """ + + if(fileflag): + self.PkgQuery(irc, msg, 'file', term) + else: + self.PkgQuery(irc, msg, 'pkg', "*" + term + "*") + + pkg = thread(wrap(pkg, [optional(('literal', ['-f'])), 'somethingWithoutSpaces'])) + + def FileQuery(self, irc, msg, term): + db = self.InitDB(); + if db is None: + irc.error("slackpkg database doesn't exist", Raise=True) + + maxresults = self.getMaxResults(msg) + cursor = db.cursor() + cursor.execute("select path, symlink from files where path glob ? order by path limit ?", (term, maxresults+1)) + + result = cursor.fetchall() + + lines = [] + + if len(result) == 0: + irc.reply("nothing found") + else: + for (file,symlink) in result: + if(symlink): + file = file + " [l]" + lines.append(file) + if(len(result) > maxresults): + lines.append("[too many results, only showing first " + str(maxresults) + "]") + irc.replies(lines, joiner=' | ') + + # subset of locate/slocate/mlocate. + def locate(self, irc, msg, args, file): + """ + + Search the package database for files named like . + This is a case-sensitive substring match (basically works like the + actual 'locate' command). You can use * for globbing. + """ + + self.FileQuery(irc, msg, '*' + file + '*') + + locate = thread(wrap(locate, ['somethingWithoutSpaces'])) + + # faux 'which' command. + def which(self, irc, msg, args, file): + """ + + Search the package database for files named like */bin/. + This is similar to the OS "which" command, except it doesn't search + a $PATH, it doesn't know whether the file has +x permission, it can + return multiple results, and you can use * for globbing. + """ + + self.FileQuery(irc, msg, '*/bin/' + file) + + which = thread(wrap(which, ['somethingWithoutSpaces'])) + +Class = SlackTools -- cgit v1.2.3