aboutsummaryrefslogtreecommitdiff
path: root/SlackTools
diff options
context:
space:
mode:
Diffstat (limited to 'SlackTools')
-rw-r--r--SlackTools/README.md1
-rw-r--r--SlackTools/__init__.py48
-rw-r--r--SlackTools/config.py36
-rw-r--r--SlackTools/local/__init__.py1
-rw-r--r--SlackTools/plugin.py202
-rw-r--r--SlackTools/plugin.py.old94
-rw-r--r--SlackTools/test.py15
7 files changed, 397 insertions, 0 deletions
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):
+ """ <packagename>
+
+ Search the Slackware package database for packages named like <packagename>.
+ 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):
+ """ <filename>
+
+ Search the Slackware package database for packages that contain files
+ named <filename>. 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] <file-or-package>
+
+ 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):
+ """ <filename>
+
+ Search the package database for files named like <filename>.
+ 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):
+ """ <filename>
+
+ Search the package database for files named like */bin/<filename>.
+ 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 <packagename> (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 <path> (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: