3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-12-24 11:42:51 +01:00

Automode plugin stub (#204)

This supports adding/removing/listing entries and saving them to a DB, but no modes are set yet.
This commit is contained in:
James Lu 2016-07-07 21:06:28 -07:00
parent 14b30b26c0
commit d365f04199
3 changed files with 188 additions and 1 deletions

View File

@ -1,2 +1,2 @@
# Automatically generated by setup.py
__version__ = '0.9-dev1-24-g50d30d4'
__version__ = '0.9-dev1-27-g14b30b2'

View File

@ -403,3 +403,7 @@ relay:
games:
# Sets the nick of the Games service, if you're using it.
nick: Games
automode:
# Sets the nick of the Automode service, if you're using it.
nick: ModeBot

183
plugins/automode.py Normal file
View File

@ -0,0 +1,183 @@
"""
automode.py - Provide simple channel ACL management by giving prefix modes to users matching
hostmasks or exttargets.
"""
import collections
import threading
import json
from pylinkirc import utils
from pylinkirc.log import log
mydesc = ("The \x02Automode\x02 plugin provides simple channel ACL management by giving prefix modes "
"to users matching hostmasks or exttargets.")
# Register ourselves as a service.
modebot = utils.registerService("Automode", desc=mydesc)
reply = modebot.reply
# Databasing variables.
dbname = utils.getDatabaseName('automode')
db = collections.defaultdict(dict)
exportdb_timer = None
def loadDB():
"""Loads the Automode database, silently creating a new one if this fails."""
global db
try:
with open(dbname, "r") as f:
db.update(json.load(f))
except (ValueError, IOError, OSError):
log.info("Automode: failed to load links database %s; using the one in"
"memory.", dbname)
def exportDB():
"""Exports the automode database."""
log.debug("Automode: exporting database to %s.", dbname)
with open(dbname, 'w') as f:
# Pretty print the JSON output for better readability.
json.dump(db, f, indent=4)
def scheduleExport(starting=False):
"""
Schedules exporting of the Automode database in a repeated loop.
"""
global exportdb_timer
if not starting:
# Export the database, unless this is being called the first
# thing after start (i.e. DB has just been loaded).
exportDB()
# TODO: possibly make delay between exports configurable
exportdb_timer = threading.Timer(30, scheduleExport)
exportdb_timer.name = 'Automode exportDB Loop'
exportdb_timer.start()
def main(irc=None):
"""Main function, called during plugin loading at start."""
# Load the relay links database.
loadDB()
# Schedule periodic exports of the automode database.
scheduleExport(starting=True)
def die(sourceirc):
"""Saves the Automode database and quit."""
exportDB()
# Kill the scheduling forexports.
global exportdb_timer
if exportdb_timer:
log.debug("Automode: cancelling exportDB timer thread %s due to die()", threading.get_ident())
exportdb_timer.cancel()
def setacc(irc, source, args):
"""<channel> <mask> <mode list OR literal ->
Assigns the given prefix mode characters to the given mask for the channel given. Extended targets are supported for masks - use this to your advantage!
Examples:
SET #channel *!*@localhost ohv
SET #channel $account v
SET #channel $oper:Network?Administrator qo
SET #staffchan $channel:#mainchan:op o
"""
irc.checkAuthenticated(source)
try:
channel, mask, modes = args
except ValueError:
reply(irc, "Error: Invalid arguments given. Needs 3: channel, mask, mode list.")
return
else:
if not utils.isChannel(channel):
reply(irc, "Error: Invalid channel name %s." % channel)
return
# Store channels case insensitively
channel = irc.toLower(channel)
# Database entries for any network+channel pair are automatically created using
# defaultdict. Note: string keys are used here instead of tuples so they can be
# exported easily as JSON.
dbentry = db[irc.name+channel]
# Otherwise, update the modes as is.
dbentry[mask] = modes
reply(irc, "Done. \x02%s\x02 now has modes \x02%s\x02 in \x02%s\x02." % (mask, modes, channel))
modebot.add_cmd(setacc, 'setaccess')
modebot.add_cmd(setacc, 'set')
modebot.add_cmd(setacc, featured=True)
def delacc(irc, source, args):
"""<channel> <mask>
Removes the Automode entry for the given mask on the given channel, if one exists.
"""
irc.checkAuthenticated(source)
try:
channel, mask = args
except ValueError:
reply(irc, "Error: Invalid arguments given. Needs 2: channel, mask")
return
dbentry = db.get(irc.name+channel)
if dbentry is None:
reply(irc, "Error: no Automode access entries exist for \x02%s\x02." % channel)
return
if mask in dbentry:
del dbentry[mask]
reply(irc, "Done. Removed the Automode access entry for \x02%s\x02 in \x02%s\x02." % (mask, channel))
else:
reply(irc, "Error: No Automode access entry for \x02%s\x02 exists in \x02%s\x02." % (mask, channel))
# Remove channels if no more entries are left.
if not dbentry:
log.debug("Automode: purging empty channel pair %s/%s", irc.name, channel)
del db[irc.name+channel]
return
modebot.add_cmd(delacc, 'delaccess')
modebot.add_cmd(delacc, 'del')
modebot.add_cmd(delacc, featured=True)
def listacc(irc, source, args):
"""<channel>
Lists all Automode entries for the given channel."""
irc.checkAuthenticated(source)
try:
channel = irc.toLower(args[0])
except IndexError:
reply(irc, "Error: Invalid arguments given. Needs 1: channel.")
return
dbentry = db.get(irc.name+channel)
if not dbentry:
reply(irc, "Error: No Automode access entries exist for \x02%s\x02." % channel)
return
else:
# Iterate over all entries and print them. Do this in private to prevent channel
# floods.
reply(irc, "Showing Automode entries for \x02%s\x02:" % channel, private=True)
for entrynum, entry in enumerate(dbentry.items(), start=1):
mask, modes = entry
reply(irc, "[%s] \x02%s\x02 has modes +\x02%s\x02" % (entrynum, mask, modes), private=True)
reply(irc, "End of Automode entries list.")
modebot.add_cmd(listacc, featured=True)
modebot.add_cmd(listacc, 'listaccess')
def save(irc, source, args):
"""takes no arguments.
Saves the Automode database to disk."""
irc.checkAuthenticated(source)
exportDB()
reply(irc, 'Done.')
modebot.add_cmd(save)