mirror of
https://github.com/jlu5/PyLink.git
synced 2025-01-12 21:22:36 +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
|
# All our checks passed, get the protocol module to connect
|
||||||
# and run the listen loop.
|
# and run the listen loop.
|
||||||
self.proto.connect()
|
self.proto.connect()
|
||||||
self.spawnMain()
|
|
||||||
log.info('(%s) Starting ping schedulers....', self.name)
|
log.info('(%s) Starting ping schedulers....', self.name)
|
||||||
self.schedulePing()
|
self.schedulePing()
|
||||||
log.info('(%s) Server ready; listening for data.', self.name)
|
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())
|
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):
|
def __repr__(self):
|
||||||
return "<classes.Irc object for %r>" % self.name
|
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
|
Calls a PyLink bot command. source is the caller's UID, and text is the
|
||||||
full, unparsed text of the message.
|
full, unparsed text of the message.
|
||||||
"""
|
"""
|
||||||
cmd_args = text.strip().split(' ')
|
world.services['pylink'].call_cmd(self, source, text)
|
||||||
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)))
|
|
||||||
|
|
||||||
def msg(self, target, text, notice=False, source=None):
|
def msg(self, target, text, notice=False, source=None):
|
||||||
"""Handy function to send messages/notices to clients. Source
|
"""Handy function to send messages/notices to clients. Source
|
||||||
@ -1129,7 +1096,6 @@ class FakeIRC(Irc):
|
|||||||
self.hookmsgs = []
|
self.hookmsgs = []
|
||||||
self.socket = None
|
self.socket = None
|
||||||
self.initVars()
|
self.initVars()
|
||||||
self.spawnMain()
|
|
||||||
self.connected = threading.Event()
|
self.connected = threading.Event()
|
||||||
self.connected.set()
|
self.connected.set()
|
||||||
|
|
||||||
|
@ -40,11 +40,6 @@ signal.signal(signal.SIGTERM, sigterm_handler)
|
|||||||
def handle_kill(irc, source, command, args):
|
def handle_kill(irc, source, command, args):
|
||||||
"""Handle KILLs to PyLink service bots, respawning them as needed."""
|
"""Handle KILLs to PyLink service bots, respawning them as needed."""
|
||||||
target = args['target']
|
target = args['target']
|
||||||
|
|
||||||
if target == irc.pseudoclient.uid:
|
|
||||||
irc.spawnMain()
|
|
||||||
return
|
|
||||||
|
|
||||||
for name, sbot in world.services.items():
|
for name, sbot in world.services.items():
|
||||||
if target == sbot.uids.get(irc.name):
|
if target == sbot.uids.get(irc.name):
|
||||||
spawn_service(irc, source, command, {'name': 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."""
|
"""Handle KICKs to the PyLink service bots, rejoining channels as needed."""
|
||||||
kicked = args['target']
|
kicked = args['target']
|
||||||
channel = args['channel']
|
channel = args['channel']
|
||||||
if kicked == irc.pseudoclient.uid or kicked in \
|
if kicked in [sbot.uids.get(irc.name) for sbot in world.services.values()]:
|
||||||
[sbot.uids.get(irc.name) for sbot in world.services.values()]:
|
|
||||||
irc.proto.join(kicked, channel)
|
irc.proto.join(kicked, channel)
|
||||||
utils.add_hook(handle_kick, 'KICK')
|
utils.add_hook(handle_kick, 'KICK')
|
||||||
|
|
||||||
@ -65,14 +59,10 @@ def handle_commands(irc, source, command, args):
|
|||||||
target = args['target']
|
target = args['target']
|
||||||
text = args['text']
|
text = args['text']
|
||||||
|
|
||||||
if target == irc.pseudoclient.uid and not irc.isInternalClient(source):
|
for sbot in world.services.values():
|
||||||
irc.called_by = source
|
if target == sbot.uids.get(irc.name):
|
||||||
irc.callCommand(source, text)
|
sbot.call_cmd(irc, source, text)
|
||||||
else:
|
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')
|
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.
|
# Track the service's UIDs on each network.
|
||||||
sbot = world.services[name]
|
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",
|
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
|
# TODO: channels should be tracked in a central database, not hardcoded
|
||||||
# in conf.
|
# in conf.
|
||||||
@ -256,6 +253,11 @@ def handle_endburst(irc, source, command, args):
|
|||||||
|
|
||||||
utils.add_hook(handle_endburst, 'ENDBURST')
|
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,
|
# 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.
|
||||||
|
|
||||||
@ -465,4 +467,3 @@ def rehash(irc, source, args):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
irc.reply("Done.")
|
irc.reply("Done.")
|
||||||
|
|
||||||
|
@ -20,53 +20,6 @@ def status(irc, source, args):
|
|||||||
irc.reply('You are not identified as anyone.')
|
irc.reply('You are not identified as anyone.')
|
||||||
irc.reply('Operator access: \x02%s\x02' % bool(irc.isOper(source)))
|
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'
|
_none = '\x1D(none)\x1D'
|
||||||
@utils.add_cmd
|
@utils.add_cmd
|
||||||
def showuser(irc, source, args):
|
def showuser(irc, source, args):
|
||||||
|
5
utils.py
5
utils.py
@ -65,10 +65,7 @@ class IncrementalUIDGenerator():
|
|||||||
|
|
||||||
def add_cmd(func, name=None):
|
def add_cmd(func, name=None):
|
||||||
"""Binds an IRC command function to the given command name."""
|
"""Binds an IRC command function to the given command name."""
|
||||||
if name is None:
|
world.services['pylink'].add_cmd(func, name=name)
|
||||||
name = func.__name__
|
|
||||||
name = name.lower()
|
|
||||||
world.commands[name].append(func)
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def add_hook(func, command):
|
def add_hook(func, command):
|
||||||
|
Loading…
Reference in New Issue
Block a user