mirror of
https://github.com/jlu5/PyLink.git
synced 2024-12-24 11:42:51 +01:00
core: migrate the main client to ServiceBot (#216)
- irc.spawnMain is dropped. Clients are now spawned in the endburst loop, after protocol negotiation completes. This allows PyLink to spawn clients with hideoper, etc., closing #194. - utils.add_cmd and irc.callCommand are now just wrappers around world.services['pylink'].add_cmd and call_cmd respectively. - coreplugin registers the main client while it is loaded up, before any commands are added.
This commit is contained in:
parent
118d76fd5a
commit
32bc5f120b
36
classes.py
36
classes.py
@ -256,7 +256,6 @@ class Irc():
|
||||
# All our checks passed, get the protocol module to connect
|
||||
# and run the listen loop.
|
||||
self.proto.connect()
|
||||
self.spawnMain()
|
||||
log.info('(%s) Starting ping schedulers....', self.name)
|
||||
self.schedulePing()
|
||||
log.info('(%s) Server ready; listening for data.', self.name)
|
||||
@ -433,23 +432,6 @@ class Irc():
|
||||
|
||||
log.debug('(%s) Ping scheduled at %s', self.name, time.time())
|
||||
|
||||
def spawnMain(self):
|
||||
"""Spawns the main PyLink client."""
|
||||
nick = self.botdata.get('nick') or 'PyLink'
|
||||
ident = self.botdata.get('ident') or 'pylink'
|
||||
host = self.serverdata["hostname"]
|
||||
log.info('(%s) Connected! Spawning main client %s.', self.name, nick)
|
||||
olduserobj = self.pseudoclient
|
||||
self.pseudoclient = self.proto.spawnClient(nick, ident, host,
|
||||
modes={("+o", None)},
|
||||
manipulatable=True,
|
||||
opertype="PyLink Service")
|
||||
for chan in self.serverdata['channels']:
|
||||
self.proto.join(self.pseudoclient.uid, chan)
|
||||
# PyLink internal hook called when spawnMain is called and the
|
||||
# contents of Irc().pseudoclient change.
|
||||
self.callHooks([self.sid, 'PYLINK_SPAWNMAIN', {'olduser': olduserobj}])
|
||||
|
||||
def __repr__(self):
|
||||
return "<classes.Irc object for %r>" % self.name
|
||||
|
||||
@ -459,22 +441,7 @@ class Irc():
|
||||
Calls a PyLink bot command. source is the caller's UID, and text is the
|
||||
full, unparsed text of the message.
|
||||
"""
|
||||
cmd_args = text.strip().split(' ')
|
||||
cmd = cmd_args[0].lower()
|
||||
cmd_args = cmd_args[1:]
|
||||
if cmd not in world.commands:
|
||||
self.msg(self.called_by or source, 'Error: Unknown command %r.' % cmd)
|
||||
log.info('(%s) Received unknown command %r from %s', self.name, cmd, self.getHostmask(source))
|
||||
return
|
||||
log.info('(%s) Calling command %r for %s', self.name, cmd, self.getHostmask(source))
|
||||
for func in world.commands[cmd]:
|
||||
try:
|
||||
func(self, source, cmd_args)
|
||||
except utils.NotAuthenticatedError:
|
||||
self.msg(self.called_by or source, 'Error: You are not authorized to perform this operation.')
|
||||
except Exception as e:
|
||||
log.exception('Unhandled exception caught in command %r', cmd)
|
||||
self.msg(self.called_by or source, 'Uncaught exception in command %r: %s: %s' % (cmd, type(e).__name__, str(e)))
|
||||
world.services['pylink'].call_cmd(self, source, text)
|
||||
|
||||
def msg(self, target, text, notice=False, source=None):
|
||||
"""Handy function to send messages/notices to clients. Source
|
||||
@ -1129,7 +1096,6 @@ class FakeIRC(Irc):
|
||||
self.hookmsgs = []
|
||||
self.socket = None
|
||||
self.initVars()
|
||||
self.spawnMain()
|
||||
self.connected = threading.Event()
|
||||
self.connected.set()
|
||||
|
||||
|
@ -40,11 +40,6 @@ signal.signal(signal.SIGTERM, sigterm_handler)
|
||||
def handle_kill(irc, source, command, args):
|
||||
"""Handle KILLs to PyLink service bots, respawning them as needed."""
|
||||
target = args['target']
|
||||
|
||||
if target == irc.pseudoclient.uid:
|
||||
irc.spawnMain()
|
||||
return
|
||||
|
||||
for name, sbot in world.services.items():
|
||||
if target == sbot.uids.get(irc.name):
|
||||
spawn_service(irc, source, command, {'name': name})
|
||||
@ -55,8 +50,7 @@ def handle_kick(irc, source, command, args):
|
||||
"""Handle KICKs to the PyLink service bots, rejoining channels as needed."""
|
||||
kicked = args['target']
|
||||
channel = args['channel']
|
||||
if kicked == irc.pseudoclient.uid or kicked in \
|
||||
[sbot.uids.get(irc.name) for sbot in world.services.values()]:
|
||||
if kicked in [sbot.uids.get(irc.name) for sbot in world.services.values()]:
|
||||
irc.proto.join(kicked, channel)
|
||||
utils.add_hook(handle_kick, 'KICK')
|
||||
|
||||
@ -65,14 +59,10 @@ def handle_commands(irc, source, command, args):
|
||||
target = args['target']
|
||||
text = args['text']
|
||||
|
||||
if target == irc.pseudoclient.uid and not irc.isInternalClient(source):
|
||||
irc.called_by = source
|
||||
irc.callCommand(source, text)
|
||||
else:
|
||||
for sbot in world.services.values():
|
||||
if target == sbot.uids.get(irc.name):
|
||||
sbot.call_cmd(irc, source, text)
|
||||
return
|
||||
for sbot in world.services.values():
|
||||
if target == sbot.uids.get(irc.name):
|
||||
sbot.call_cmd(irc, source, text)
|
||||
return
|
||||
|
||||
utils.add_hook(handle_commands, 'PRIVMSG')
|
||||
|
||||
@ -223,9 +213,16 @@ def spawn_service(irc, source, command, args):
|
||||
|
||||
# Track the service's UIDs on each network.
|
||||
sbot = world.services[name]
|
||||
sbot.uids[irc.name] = u = irc.proto.spawnClient(sbot.nick, sbot.ident,
|
||||
userobj = irc.proto.spawnClient(sbot.nick, sbot.ident,
|
||||
host, modes=modes, opertype="PyLink Service",
|
||||
manipulatable=sbot.manipulatable).uid
|
||||
manipulatable=sbot.manipulatable)
|
||||
|
||||
sbot.uids[irc.name] = u = userobj.uid
|
||||
|
||||
# Special case: if this is the main PyLink client being spawned,
|
||||
# assign this as irc.pseudoclient.
|
||||
if name == 'pylink':
|
||||
irc.pseudoclient = userobj
|
||||
|
||||
# TODO: channels should be tracked in a central database, not hardcoded
|
||||
# in conf.
|
||||
@ -256,6 +253,11 @@ def handle_endburst(irc, source, command, args):
|
||||
|
||||
utils.add_hook(handle_endburst, 'ENDBURST')
|
||||
|
||||
# Register the main PyLink service. All command definitions MUST go after this!
|
||||
mynick = conf.conf['bot'].get("nick", "PyLink")
|
||||
myident = conf.conf['bot'].get("ident", "pylink")
|
||||
utils.registerService('pylink', nick=mynick, ident=myident)
|
||||
|
||||
# Essential, core commands go here so that the "commands" plugin with less-important,
|
||||
# but still generic functions can be reloaded.
|
||||
|
||||
@ -465,4 +467,3 @@ def rehash(irc, source, args):
|
||||
return
|
||||
else:
|
||||
irc.reply("Done.")
|
||||
|
||||
|
@ -20,53 +20,6 @@ def status(irc, source, args):
|
||||
irc.reply('You are not identified as anyone.')
|
||||
irc.reply('Operator access: \x02%s\x02' % bool(irc.isOper(source)))
|
||||
|
||||
def listcommands(irc, source, args):
|
||||
"""takes no arguments.
|
||||
|
||||
Returns a list of available commands PyLink has to offer."""
|
||||
cmds = list(world.commands.keys())
|
||||
cmds.sort()
|
||||
for idx, cmd in enumerate(cmds):
|
||||
nfuncs = len(world.commands[cmd])
|
||||
if nfuncs > 1:
|
||||
cmds[idx] = '%s(x%s)' % (cmd, nfuncs)
|
||||
irc.reply('Available commands include: %s' % ', '.join(cmds))
|
||||
irc.reply('To see help on a specific command, type \x02help <command>\x02.')
|
||||
utils.add_cmd(listcommands, 'list')
|
||||
|
||||
@utils.add_cmd
|
||||
def help(irc, source, args):
|
||||
"""<command>
|
||||
|
||||
Gives help for <command>, if it is available."""
|
||||
try:
|
||||
command = args[0].lower()
|
||||
except IndexError: # No argument given, just return 'list' output
|
||||
listcommands(irc, source, args)
|
||||
return
|
||||
if command not in world.commands:
|
||||
irc.msg(source, 'Error: Unknown command %r.' % command)
|
||||
return
|
||||
else:
|
||||
funcs = world.commands[command]
|
||||
if len(funcs) > 1:
|
||||
irc.reply('The following \x02%s\x02 plugins bind to the \x02%s\x02 command: %s'
|
||||
% (len(funcs), command, ', '.join([func.__module__ for func in funcs])))
|
||||
for func in funcs:
|
||||
doc = func.__doc__
|
||||
mod = func.__module__
|
||||
if doc:
|
||||
lines = doc.split('\n')
|
||||
# Bold the first line, which usually just tells you what
|
||||
# arguments the command takes.
|
||||
lines[0] = '\x02%s %s\x02 (plugin: %r)' % (command, lines[0], mod)
|
||||
for line in lines:
|
||||
irc.reply(line.strip())
|
||||
else:
|
||||
irc.msg(source, "Error: Command %r (from plugin %r) "
|
||||
"doesn't offer any help." % (command, mod))
|
||||
return
|
||||
|
||||
_none = '\x1D(none)\x1D'
|
||||
@utils.add_cmd
|
||||
def showuser(irc, source, args):
|
||||
|
5
utils.py
5
utils.py
@ -65,10 +65,7 @@ class IncrementalUIDGenerator():
|
||||
|
||||
def add_cmd(func, name=None):
|
||||
"""Binds an IRC command function to the given command name."""
|
||||
if name is None:
|
||||
name = func.__name__
|
||||
name = name.lower()
|
||||
world.commands[name].append(func)
|
||||
world.services['pylink'].add_cmd(func, name=name)
|
||||
return func
|
||||
|
||||
def add_hook(func, command):
|
||||
|
Loading…
Reference in New Issue
Block a user