3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-12-26 04:32:51 +01:00

Begin work on service bot abstraction (#216)

- State-keeping is done by coreplugin
- utils.registerService() introduced
- new PYLINK_NEW_SERVICE hook introduced
This commit is contained in:
James Lu 2016-05-14 09:55:46 -07:00
parent ce95e15897
commit 7d11f8c7e0
3 changed files with 114 additions and 2 deletions

View File

@ -186,6 +186,55 @@ def handle_version(irc, source, command, args):
irc.proto.numeric(irc.sid, 351, source, fullversion) irc.proto.numeric(irc.sid, 351, source, fullversion)
utils.add_hook(handle_version, 'VERSION') utils.add_hook(handle_version, 'VERSION')
def handle_newservice(irc, source, command, args):
"""Handles new service bot introductions."""
if not irc.connected.is_set():
return
name = args['name']
ident = irc.botdata.get('ident') or 'pylink'
host = irc.serverdata["hostname"]
modes = []
for mode in ('oper', 'hideoper', 'hidechans'):
mode = irc.cmodes.get(mode)
if mode:
modes.append((mode, None))
# Track the service's UIDs on each network.
service = world.services[name]
service.uids[irc.name] = u = irc.proto.spawnClient(name, name,
irc.serverdata['hostname'], modes=modes, opertype="PyLink Service")
# TODO: channels should be tracked in a central database, not hardcoded
# in conf.
for chan in irc.serverdata['channels']:
irc.proto.join(u.uid, chan)
utils.add_hook(handle_newservice, 'PYLINK_NEW_SERVICE')
def handle_disconnect(irc, source, command, args):
"""Handles network disconnections."""
for name, sbot in world.services.items():
try:
del sbot.uids[irc.name]
log.debug("coreplugin: removing uids[%s] from service bot %s", irc.name, sbot.name)
except KeyError:
continue
utils.add_hook(handle_disconnect, 'PYLINK_DISCONNECT')
def handle_endburst(irc, source, command, args):
"""Handles network bursts."""
if source == irc.uplink:
log.debug('(%s): spawning service bots now.')
# We just connected. Burst all our registered services.
for name, sbot in world.services.items():
handle_newservice(irc, source, command, {'name': name})
utils.add_hook(handle_endburst, 'ENDBURST')
# Essential, core commands go here so that the "commands" plugin with less-important, # Essential, core commands go here so that the "commands" plugin with less-important,
# but still generic functions can be reloaded. # but still generic functions can be reloaded.

View File

@ -9,6 +9,7 @@ import string
import re import re
import importlib import importlib
import os import os
import collections
from log import log from log import log
import world import world
@ -141,3 +142,64 @@ def getDatabaseName(dbname):
dbname += '-%s' % conf.confname dbname += '-%s' % conf.confname
dbname += '.db' dbname += '.db'
return dbname return dbname
class ServiceBot():
def __init__(self, name, default_help=True, default_request=True, default_list=True):
self.name = name
# We make the command definitions a dict of lists of functions. Multiple
# plugins are actually allowed to bind to one function name; this just causes
# them to be called in the order that they are bound.
self.commands = collections.defaultdict(list)
# This tracks the UIDs of the service bot on different networks, as they are
# spawned.
self.uids = {}
if default_help:
self.add_cmd(self.help)
if default_request:
self.add_cmd(self.request)
self.add_cmd(self.remove)
if default_list:
self.add_cmd(self.listcommands, 'list')
def spawn(self, irc=None):
# Spawn the new service by calling the PYLINK_NEW_SERVICE hook,
# which is handled by coreplugin.
if irc is None:
for irc in world.networkobjects.values():
irc.callHooks([None, 'PYLINK_NEW_SERVICE', {'name': self.name}])
else:
raise NotImplementedError("Network specific plugins not supported yet.")
def add_cmd(self, func, name=None):
"""Binds an IRC command function to the given command name."""
if name is None:
name = func.__name__
name = name.lower()
self.commands[name].append(func)
return func
def help(self, irc, source, args):
irc.reply("Help command stub called.")
def request(self, irc, source, args):
irc.reply("Request command stub called.")
def remove(self, irc, source, args):
irc.reply("Remove command stub called.")
def listcommands(self, irc, source, args):
irc.reply("List command stub called.")
def registerService(name, *args, **kwargs):
name = name.lower()
if name in world.services:
raise ValueError("Service name %s is already bound!" % name)
world.services[name] = sbot = ServiceBot(name, *args, **kwargs)
sbot.spawn()

View File

@ -14,13 +14,14 @@ testing = True
# Sets the default protocol module to use with tests. # Sets the default protocol module to use with tests.
testing_ircd = 'inspircd' testing_ircd = 'inspircd'
global commands, hooks
# This should be a mapping of command names to functions
commands = defaultdict(list) commands = defaultdict(list)
hooks = defaultdict(list) hooks = defaultdict(list)
networkobjects = {} networkobjects = {}
plugins = {} plugins = {}
whois_handlers = [] whois_handlers = []
services = {}
started = threading.Event() started = threading.Event()
plugins_folder = os.path.join(os.getcwd(), 'plugins') plugins_folder = os.path.join(os.getcwd(), 'plugins')