3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-23 19:19:31 +01:00

Support pre-auth irc.pseudoclient enumeration & configurable altnicks

Closes #516. Closes #288.
This commit is contained in:
James Lu 2017-08-21 23:05:27 -07:00
parent 04cfa9c93e
commit 0bb4a35c6f
2 changed files with 56 additions and 50 deletions

View File

@ -22,34 +22,36 @@ def spawn_service(irc, source, command, args):
# Get the ServiceBot object.
sbot = world.services[name]
nick = sbot.get_nick(irc)
ident = sbot.get_ident(irc)
host = sbot.get_host(irc)
realname = sbot.get_realname(irc)
# Spawning service clients with these umodes where supported. servprotect usage is a
# configuration option.
preferred_modes = ['oper', 'hideoper', 'hidechans', 'invisible', 'bot']
modes = []
if conf.conf['pylink'].get('protect_services'):
preferred_modes.append('servprotect')
for mode in preferred_modes:
mode = irc.umodes.get(mode)
if mode:
modes.append((mode, None))
# Track the service's UIDs on each network.
log.debug('(%s) spawn_service: Using nick %s for service %s', irc.name, nick, name)
u = irc.nick_to_uid(nick)
if u and irc.is_internal_client(u): # If an internal client exists, reuse it.
log.debug('(%s) spawn_service: Using existing client %s/%s', irc.name, u, nick)
userobj = irc.users[u]
if name == 'pylink' and irc.pseudoclient:
# irc.pseudoclient already exists, for protocols like clientbot
log.debug('(%s) spawn_service: Using existing nick %r for service %r', irc.name, irc.pseudoclient.nick, name)
userobj = irc.pseudoclient
userobj.opertype = "PyLink Service"
userobj.manipulatable = sbot.manipulatable
else:
log.debug('(%s) spawn_service: Spawning new client %s', irc.name, nick)
# No client exists, spawn a new one
nick = sbot.get_nick(irc)
ident = sbot.get_ident(irc)
host = sbot.get_host(irc)
realname = sbot.get_realname(irc)
# Spawning service clients with these umodes where supported. servprotect usage is a
# configuration option.
preferred_modes = ['oper', 'hideoper', 'hidechans', 'invisible', 'bot']
modes = []
if conf.conf['pylink'].get('protect_services'):
preferred_modes.append('servprotect')
for mode in preferred_modes:
mode = irc.umodes.get(mode)
if mode:
modes.append((mode, None))
# Track the service's UIDs on each network.
log.debug('(%s) spawn_service: Spawning new client %s for service %s', irc.name, nick, name)
userobj = irc.spawn_client(nick, ident, host, modes=modes, opertype="PyLink Service",
realname=realname, manipulatable=sbot.manipulatable)
realname=realname, manipulatable=sbot.manipulatable)
# Store the service name in the User object for easier access.
userobj.service = name
@ -58,7 +60,7 @@ def spawn_service(irc, source, command, args):
# Special case: if this is the main PyLink client being spawned,
# assign this as irc.pseudoclient.
if name == 'pylink':
if name == 'pylink' and not irc.pseudoclient:
log.debug('(%s) spawn_service: irc.pseudoclient set to UID %s', irc.name, u)
irc.pseudoclient = userobj

View File

@ -53,6 +53,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
self.handle_463 = self.handle_464 = self.handle_465 = self.handle_error
self._use_builtin_005_handling = True
self._nick_fails = 0
self.hook_map = {'ACCOUNT': 'CLIENT_SERVICES_LOGIN'}
@ -92,14 +93,19 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
self._cap_timer = threading.Timer(self.serverdata.get('sasl_timeout') or 15, _do_cap_end_wrapper)
self._cap_timer.start()
# This is a really gross hack to get the defined NICK/IDENT/HOST/GECOS.
# But this connection stuff is done before any of the spawn_client stuff in
# services_support fires.
self.conf_nick = self.serverdata.get('pylink_nick') or conf.conf["bot"].get("nick", "PyLink")
f('NICK %s' % (self.conf_nick))
ident = self.serverdata.get('pylink_ident') or conf.conf["bot"].get("ident", "pylink")
f('USER %s 8 * :%s' % (ident, # TODO: per net realnames or hostnames aren't implemented yet.
conf.conf["bot"].get("realname", "PyLink Clientbot")))
# Log in to IRC and set our irc.pseudoclient object.
sbot = world.services['pylink']
self._nick_fails = 0
nick = sbot.get_nick(self)
ident = sbot.get_ident(self)
realname = sbot.get_realname(self)
f('NICK %s' % nick)
f('USER %s 8 * :%s' % (ident, realname))
self.pseudoclient = User(nick, int(time.time()), self.uidgen.next_uid(prefix='@ClientbotInternal'), self.sid,
ident=ident, realname=realname)
self.users[self.pseudoclient.uid] = self.pseudoclient
# Note: clientbot clients are initialized with umode +i by default
def spawn_client(self, nick, ident='unknown', host='unknown.host', realhost=None, modes={('i', None)},
@ -116,7 +122,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
log.debug('(%s) spawn_client stub called, saving nick %s as PUID %s', self.name, nick, uid)
u = self.users[uid] = User(nick, ts, uid, server, ident=ident, host=host, realname=realname,
manipulatable=manipulatable, realhost=realhost, ip=ip)
manipulatable=manipulatable, realhost=realhost, ip=ip)
self.servers[server].users.add(uid)
self.apply_modes(uid, modes)
@ -794,12 +800,12 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
def handle_433(self, source, command, args):
# <- :millennium.overdrivenetworks.com 433 * ice :Nickname is already in use.
# HACK: I don't like modifying the config entries raw, but this is difficult because
# irc.pseudoclient doesn't exist as an attribute until we get run the ENDBURST stuff
# in service_support (this is mapped to 005 here).
self.conf_nick += '_'
self.serverdata['pylink_nick'] = self.conf_nick
self.send('NICK %s' % self.conf_nick)
self._nick_fails += 1
newnick = self.pseudoclient.nick = world.services['pylink'].get_nick(self, fails=self._nick_fails)
log.debug('(%s) _nick_fails = %s, trying new nick %r', self.name, self._nick_fails, newnick)
self.send('NICK %s' % newnick)
handle_432 = handle_437 = handle_433
def handle_account(self, source, command, args):
@ -937,17 +943,15 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
"""Handles NICK changes."""
# <- :GL|!~GL@127.0.0.1 NICK :GL_
if not self.pseudoclient:
if not self.connected.is_set():
# We haven't properly logged on yet, so any initial NICK should be treated as a forced
# nick change for US. For example, this clause is used to handle forced nick changes
# nick change for us. For example, this clause is used to handle forced nick changes
# sent by ZNC, when the login nick and the actual IRC nick of the bouncer differ.
# HACK: change the nick config entry so services_support knows what our main
# pseudoclient is called.
oldnick = self.serverdata['pylink_nick']
self.serverdata['pylink_nick'] = self.conf_nick = args[0]
log.debug('(%s) Pre-auth FNC: Forcing configured nick to %s from %s', self.name, args[0], oldnick)
self.pseudoclient.nick = args[0]
log.debug('(%s) Pre-auth FNC: Changing our nick to %s', self.name, args[0])
return
elif source == self.pseudoclient.uid:
self._nick_fails = 0 # Our last nick change succeeded.
oldnick = self.users[source].nick
self.users[source].nick = args[0]