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/README.md | 1 + SlackTools/__init__.py | 48 ++++++++++ SlackTools/config.py | 36 ++++++++ SlackTools/local/__init__.py | 1 + SlackTools/plugin.py | 202 +++++++++++++++++++++++++++++++++++++++++++ SlackTools/plugin.py.old | 94 ++++++++++++++++++++ SlackTools/test.py | 15 ++++ 7 files changed, 397 insertions(+) create mode 100644 SlackTools/README.md create mode 100644 SlackTools/__init__.py create mode 100644 SlackTools/config.py create mode 100644 SlackTools/local/__init__.py create mode 100644 SlackTools/plugin.py create mode 100644 SlackTools/plugin.py.old create mode 100644 SlackTools/test.py (limited to 'SlackTools') diff --git a/SlackTools/README.md b/SlackTools/README.md new file mode 100644 index 0000000..a694e68 --- /dev/null +++ b/SlackTools/README.md @@ -0,0 +1 @@ +Provides Slackware package database lookups. diff --git a/SlackTools/__init__.py b/SlackTools/__init__.py new file mode 100644 index 0000000..fe66e85 --- /dev/null +++ b/SlackTools/__init__.py @@ -0,0 +1,48 @@ +### +# Copyright (c) 2021, B. Watson +# All rights reserved. +# +# +### + +""" +SlackTools: Provides Unix manpage lookups +""" + +import sys +import supybot +from supybot import world + +# Use this for the version of this plugin. +__version__ = "0.0.1" + +# XXX Replace this with an appropriate author or supybot.Author instance. +__author__ = supybot.Author('B. Watson', 'Urchlay', 'yalhcru@gmail.com') + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +# This is a url where the most recent plugin package can be downloaded. +__url__ = '' + +from . import config +from . import plugin +if sys.version_info >= (3, 4): + from importlib import reload +else: + from imp import reload +# In case we're being reloaded. +reload(config) +reload(plugin) +# 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: + from . import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/SlackTools/config.py b/SlackTools/config.py new file mode 100644 index 0000000..f660d44 --- /dev/null +++ b/SlackTools/config.py @@ -0,0 +1,36 @@ +### +# Copyright (c) 2021, B. Watson +# All rights reserved. +# +# +### + +from supybot import conf, registry +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('SlackTools') +except: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified themself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import expect, anything, something, yn + conf.registerPlugin('SlackTools', True) + + +SlackTools = conf.registerPlugin('SlackTools') +# This is where your configuration variables (if any) should go. For example: +# conf.registerGlobalValue(SlackTools, 'someConfigVariableName', +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) + +conf.registerGlobalValue(SlackTools, 'dbpath', + registry.String("/home/slackfacts/supybot/SlackTools.sqlite3", _("""Path to sqite3 database."""))) + +conf.registerGlobalValue(SlackTools, 'maxresults', + registry.Integer(5, _("""Maximum number of results to send to client."""))) diff --git a/SlackTools/local/__init__.py b/SlackTools/local/__init__.py new file mode 100644 index 0000000..e86e97b --- /dev/null +++ b/SlackTools/local/__init__.py @@ -0,0 +1 @@ +# Stub so local is a module, used for third-party modules 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 diff --git a/SlackTools/plugin.py.old b/SlackTools/plugin.py.old new file mode 100644 index 0000000..f50a55d --- /dev/null +++ b/SlackTools/plugin.py.old @@ -0,0 +1,94 @@ +### +# 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('Slackpkg') +except ImportError: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +class Slackpkg(callbacks.Plugin): + """Provides Slackware package db lookups""" + threaded = True + + db = None + + 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 + + # search for packages by name + def pkgsearch(self, irc, msg, args, pkg): + """ Usage: pkg (wildcards allowed)""" + + db = self.InitDB(); + if db is None: + irc.error("slackpkg database doesn't exist", Raise=True) + + maxresults = self.registryValue('maxresults') + cursor = db.cursor() + 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 ?", (pkg, maxresults+1)) + result = cursor.fetchall() + + lines = [] + + if len(result) == 0: + irc.reply("no matching packages") + else: + for (category, pkg, descrip) in result: + lines.append(format('%s/%s - %s', category, pkg, descrip)) + if(len(result) > maxresults): + lines.append("[too many results, only showing first " + str(maxresults) + "]") + irc.replies(lines, joiner=' | ') + + pkgsearch = thread(wrap(pkg, ['somethingWithoutSpaces'])) + + # search for packages by contents + def pkgfile(self, irc, msg, args, pkg): + """ Usage: pkgfile (wildcards allowed)""" + + db = self.InitDB(); + if db is None: + irc.error("slackpkg database doesn't exist", Raise=True) + + maxresults = self.registryValue('maxresults') + cursor = db.cursor() + 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 ?", (pkg, maxresults+1)) + result = cursor.fetchall() + + lines = [] + + if len(result) == 0: + irc.reply("no matching packages") + else: + for (category, pkg, descrip) in result: + lines.append(format('%s/%s - %s', category, pkg, descrip)) + if(len(result) > maxresults): + lines.append("[too many results, only showing first " + str(maxresults) + "]") + irc.replies(lines, joiner=' | ') + + pkgfile = thread(wrap(pkgfile, ['somethingWithoutSpaces'])) + + #def which(self, irc, msg, args, filename): + +Class = Slackpkg diff --git a/SlackTools/test.py b/SlackTools/test.py new file mode 100644 index 0000000..14d770b --- /dev/null +++ b/SlackTools/test.py @@ -0,0 +1,15 @@ +### +# Copyright (c) 2021, B. Watson +# All rights reserved. +# +# +### + +from supybot.test import * + + +class SlackToolsTestCase(PluginTestCase): + plugins = ('SlackTools',) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: -- cgit v1.2.3