Services: Add support for network-specific password.

This commit is contained in:
Valentin Lorentz 2019-08-25 14:00:11 +02:00
parent c07086d7dc
commit b65d78518c
5 changed files with 112 additions and 54 deletions

View File

@ -205,14 +205,24 @@ class Config(callbacks.Plugin):
network=network.network, channel=channel, check=False)
value = str(group) or ' '
if addGlobal and not irc.nested:
value = _(
'Global: %(global_value)s; '
'%(channel_name)s @ %(network_name)s: %(channel_value)s') % {
'global_value': global_value,
'channel_name': msg.channel,
'network_name': irc.network,
'channel_value': value,
}
if global_group._channelValue and channel:
# TODO: also show the network value when relevant
value = _(
'Global: %(global_value)s; '
'%(channel_name)s @ %(network_name)s: %(channel_value)s') % {
'global_value': global_value,
'channel_name': msg.channel,
'network_name': irc.network,
'channel_value': value,
}
elif global_group._networkValue and network:
value = _(
'Global: %(global_value)s; '
'%(network_name)s: %(network_value)s') % {
'global_value': global_value,
'network_name': irc.network,
'network_value': value,
}
if hasattr(global_group, 'value'):
if not global_group._private:
return (value, None)
@ -330,7 +340,7 @@ class Config(callbacks.Plugin):
(value, private) = self._getValue(
irc, msg, group, network=irc,
channel=msg.channel,
addGlobal=group._channelValue)
addGlobal=group._channelValue or group._networkValue)
irc.reply(value, private=private)
config = wrap(config, ['settableConfigVar', additional('text')])

View File

@ -38,8 +38,8 @@ def registerNick(nick, password=''):
p = conf.supybot.plugins.Services.Nickserv.get('password')
h = _('Determines what password the bot will use with NickServ when ' \
'identifying as %s.') % nick
v = conf.registerGlobalValue(p, nick,
registry.String(password, h, private=True))
v = conf.registerNetworkValue(p, nick,
registry.String(password, h, private=True))
if password:
v.setValue(password)
@ -65,7 +65,7 @@ class ValidNickSet(conf.ValidNicks):
List = ircutils.IrcSet
Services = conf.registerPlugin('Services')
conf.registerGlobalValue(Services, 'nicks',
conf.registerNetworkValue(Services, 'nicks',
ValidNickSet([], _("""Determines what nicks the bot will use with
services.""")))
@ -76,19 +76,19 @@ conf.registerGlobalValue(Services, 'disabledNetworks',
Networks(_('QuakeNet').split(), _("""Determines what networks this plugin
will be disabled on.""")))
conf.registerGlobalValue(Services, 'noJoinsUntilIdentified',
conf.registerNetworkValue(Services, 'noJoinsUntilIdentified',
registry.Boolean(False, _("""Determines whether the bot will not join any
channels until it is identified. This may be useful, for instances, if
you have a vhost that isn't set until you're identified, or if you're
joining +r channels that won't allow you to join unless you identify.""")))
conf.registerGlobalValue(Services, 'ghostDelay',
conf.registerNetworkValue(Services, 'ghostDelay',
registry.NonNegativeInteger(60, _("""Determines how many seconds the bot will
wait between successive GHOST attempts. Set this to 0 to disable GHOST.""")))
conf.registerGlobalValue(Services, 'NickServ',
conf.registerNetworkValue(Services, 'NickServ',
ValidNickOrEmptyString('NickServ', _("""Determines what nick the 'NickServ' service
has.""")))
conf.registerGroup(Services.NickServ, 'password')
conf.registerGlobalValue(Services, 'ChanServ',
conf.registerNetworkValue(Services, 'ChanServ',
ValidNickOrEmptyString('ChanServ', _("""Determines what nick the 'ChanServ' service
has.""")))
conf.registerChannelValue(Services.ChanServ, 'password',

View File

@ -55,7 +55,7 @@ class Services(callbacks.Plugin):
def __init__(self, irc):
self.__parent = super(Services, self)
self.__parent.__init__(irc)
for nick in self.registryValue('nicks'):
for nick in self.registryValue('nicks', network=irc.network):
config.registerNick(nick)
self.reset()
@ -75,7 +75,7 @@ class Services(callbacks.Plugin):
def outFilter(self, irc, msg):
if msg.command == 'JOIN' and not self.disabled(irc):
if not self.identified:
if self.registryValue('noJoinsUntilIdentified'):
if self.registryValue('noJoinsUntilIdentified', network=irc.network):
self.log.info('Holding JOIN to %s @ %s until identified.',
msg.channel, irc.network)
self.waitingJoins.setdefault(irc.network, [])
@ -90,25 +90,25 @@ class Services(callbacks.Plugin):
else:
return network_nick
def _getNickServPassword(self, nick):
def _getNickServPassword(self, nick, network):
# This should later be nick-specific.
assert nick in self.registryValue('nicks')
return self.registryValue('NickServ.password.%s' % nick)
assert nick in self.registryValue('nicks', network=network)
return self.registryValue('NickServ.password.%s' % nick, network=network)
def _setNickServPassword(self, nick, password):
def _setNickServPassword(self, nick, password, network):
# This also should be nick-specific.
assert nick in self.registryValue('nicks')
self.setRegistryValue('NickServ.password.%s' % nick, password)
assert nick in self.registryValue('nicks', network=network)
self.setRegistryValue('NickServ.password.%s' % nick, password, network=network)
def _doIdentify(self, irc, nick=None):
if self.disabled(irc):
return
if nick is None:
nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'):
if nick not in self.registryValue('nicks', network=irc.network):
return
nickserv = self.registryValue('NickServ')
password = self._getNickServPassword(nick)
nickserv = self.registryValue('NickServ', network=irc.network)
password = self._getNickServPassword(nick, irc.network)
if not nickserv or not password:
s = 'Tried to identify without a NickServ or password set.'
self.log.warning(s)
@ -127,11 +127,11 @@ class Services(callbacks.Plugin):
return
if nick is None:
nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'):
if nick not in self.registryValue('nicks', network=irc.network):
return
nickserv = self.registryValue('NickServ')
password = self._getNickServPassword(nick)
ghostDelay = self.registryValue('ghostDelay')
nickserv = self.registryValue('NickServ', network=irc.network)
password = self._getNickServPassword(nick, irc.network)
ghostDelay = self.registryValue('ghostDelay', network=irc.network)
if not ghostDelay:
return
if not nickserv or not password:
@ -157,11 +157,11 @@ class Services(callbacks.Plugin):
if self.disabled(irc):
return
nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'):
if nick not in self.registryValue('nicks', network=irc.network):
return
nickserv = self.registryValue('NickServ')
password = self._getNickServPassword(nick)
ghostDelay = self.registryValue('ghostDelay')
nickserv = self.registryValue('NickServ', network=irc.network)
password = self._getNickServPassword(nick, irc.network)
ghostDelay = self.registryValue('ghostDelay', network=irc.network)
if not ghostDelay:
return
if nick and nickserv and password and \
@ -181,13 +181,13 @@ class Services(callbacks.Plugin):
if self.disabled(irc):
return
nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'):
if nick not in self.registryValue('nicks', network=irc.network):
return
nickserv = self.registryValue('NickServ')
nickserv = self.registryValue('NickServ', network=irc.network)
if not nickserv:
self.log.warning('NickServ is unset, cannot identify.')
return
password = self._getNickServPassword(nick)
password = self._getNickServPassword(nick, irc.network)
if not password:
self.log.warning('Password for %s is unset, cannot identify.',nick)
return
@ -205,10 +205,10 @@ class Services(callbacks.Plugin):
if self.disabled(irc):
return
nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'):
if nick not in self.registryValue('nicks', network=irc.network):
return
if nick and irc.afterConnect:
password = self._getNickServPassword(nick)
password = self._getNickServPassword(nick, irc.network)
if not password:
return
self._doGhost(irc)
@ -232,8 +232,8 @@ class Services(callbacks.Plugin):
def doNotice(self, irc, msg):
if irc.afterConnect:
nickserv = self.registryValue('NickServ')
chanserv = self.registryValue('ChanServ')
nickserv = self.registryValue('NickServ', network=irc.network)
chanserv = self.registryValue('ChanServ', network=irc.network)
if nickserv and ircutils.strEqual(msg.nick, nickserv):
self.doNickservNotice(irc, msg)
elif chanserv and ircutils.strEqual(msg.nick, chanserv):
@ -298,7 +298,7 @@ class Services(callbacks.Plugin):
'Resetting password to empty.' % on
self.log.warning(log)
self.sentGhost = time.time()
self._setNickServPassword(nick, '')
self._setNickServPassword(nick, '', irc.network)
elif self._ghosted(irc, s):
self.log.info('Received "GHOST succeeded" from NickServ %s.', on)
self.sentGhost = None
@ -355,7 +355,7 @@ class Services(callbacks.Plugin):
def checkPrivileges(self, irc, channel):
if self.disabled(irc):
return
chanserv = self.registryValue('ChanServ')
chanserv = self.registryValue('ChanServ', network=irc.network)
on = 'on %s' % irc.network
if chanserv and self.registryValue('ChanServ.op', channel, irc.network):
if irc.nick not in irc.state.channels[channel].ops:
@ -376,7 +376,7 @@ class Services(callbacks.Plugin):
def doMode(self, irc, msg):
if self.disabled(irc):
return
chanserv = self.registryValue('ChanServ')
chanserv = self.registryValue('ChanServ', network=irc.network)
on = 'on %s' % irc.network
if ircutils.strEqual(msg.nick, chanserv):
channel = msg.args[0]
@ -406,7 +406,7 @@ class Services(callbacks.Plugin):
self.__parent.callCommand(command, irc, msg, *args, **kwargs)
def _chanservCommand(self, irc, channel, command, log=False):
chanserv = self.registryValue('ChanServ')
chanserv = self.registryValue('ChanServ', network=irc.network)
if chanserv:
msg = ircmsgs.privmsg(chanserv,
' '.join([command, channel]))
@ -493,7 +493,8 @@ class Services(callbacks.Plugin):
invite = wrap(invite, [('checkChannelCapability', 'op'), 'inChannel'])
def doInvite(self, irc, msg):
if ircutils.strEqual(msg.nick, self.registryValue('ChanServ')):
if ircutils.strEqual(
msg.nick, self.registryValue('ChanServ', etwork=irc.network)):
channel = msg.args[1]
on = 'on %s' % irc.network
networkGroup = conf.supybot.networks.get(irc.network)
@ -506,8 +507,8 @@ class Services(callbacks.Plugin):
Identifies with NickServ using the current nick.
"""
if self.registryValue('NickServ'):
if irc.nick in self.registryValue('nicks'):
if self.registryValue('NickServ', network=irc.network):
if irc.nick in self.registryValue('nicks', network=irc.network):
self._doIdentify(irc, irc.nick)
irc.replySuccess()
else:
@ -525,7 +526,7 @@ class Services(callbacks.Plugin):
Ghosts the bot's given nick and takes it. If no nick is given,
ghosts the bot's configured nick and takes it.
"""
if self.registryValue('NickServ'):
if self.registryValue('NickServ', network=irc.network):
if not nick:
nick = self._getNick(irc.network)
if ircutils.strEqual(nick, irc.nick):
@ -547,13 +548,17 @@ class Services(callbacks.Plugin):
"""
if not password:
try:
self.registryValue('nicks').remove(nick)
v = self.registryValue('nicks', network=irc.network).copy()
v.remove(nick)
self.setRegistryValue('nicks', value=v, network=irc.network)
irc.replySuccess()
except KeyError:
irc.error(_('That nick was not configured with a password.'))
return
else:
self.registryValue('nicks').add(nick)
v = self.registryValue('nicks', network=irc.network).copy()
v.add(nick)
self.setRegistryValue('nicks', value=v, network=irc.network)
config.registerNick(nick, password)
irc.replySuccess()
password = wrap(password, [('checkCapability', 'admin'),
@ -566,7 +571,7 @@ class Services(callbacks.Plugin):
Returns the nicks that this plugin is configured to identify and ghost
with.
"""
L = list(self.registryValue('nicks'))
L = list(self.registryValue('nicks', network=irc.network))
if L:
utils.sortBy(ircutils.toLower, L)
irc.reply(format('%L', L))

View File

@ -30,7 +30,7 @@
from supybot.test import *
class ServicesTestCase(PluginTestCase):
plugins = ('Services',)
plugins = ('Services', 'Config')
config = {
'plugins.Services.NickServ': 'NickServ',
'plugins.Services.ChanServ': 'ChanServ',
@ -48,6 +48,33 @@ class ServicesTestCase(PluginTestCase):
self.failUnless(m.args[0] == 'NickServ')
self.failUnless(m.args[1].lower() == 'identify biff')
def testPasswordConfg(self):
self.assertNotError('config plugins.Services.nicks ""')
self.assertNotError('config network plugins.Services.nicks ""')
self.assertNotError('services password %s bar' % self.nick)
self.assertResponse(
'config plugins.Services.nicks',
'Global: ; test: %s' % self.nick)
self.assertResponse(
'config plugins.Services.nickserv.password.%s' % self.nick,
'Global: bar; test: bar')
self.assertNotError(
'config network plugins.Services.nickserv.password.%s bar2'
% self.nick)
self.assertResponse(
'config plugins.Services.nickserv.password.%s' % self.nick,
'Global: bar; test: bar2')
self.assertResponse(
'config plugins.Services.nickserv.password.%s' % self.nick,
'Global: bar; test: bar2')
m = self.assertNotError('services identify')
self.failUnless(m.args[0] == 'NickServ')
self.failUnless(m.args[1].lower() == 'identify bar2')
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -87,6 +87,21 @@ def registerGlobalValue(group, name, value):
value._channelValue = False
return group.register(name, value)
def registerNetworkValue(group, name, value):
value._supplyDefault = True
value._networkValue = True
value._channelValue = False
g = group.register(name, value)
gname = g._name.lower()
for name in registry._cache.keys():
if name.lower().startswith(gname) and len(gname) < len(name):
name = name[len(gname)+1:] # +1 for .
parts = registry.split(name)
if len(parts) == 1 and parts[0] and ircutils.isChannel(parts[0]):
# This gets the network values so they always persist.
g.get(parts[0])()
return g
def registerChannelValue(group, name, value, opSettable=True):
value._supplyDefault = True
value._networkValue = True
@ -106,6 +121,7 @@ def registerChannelValue(group, name, value, opSettable=True):
elif len(parts) == 1 and parts[0] and ircutils.isChannel(parts[0]):
# Old-style variant of the above, without a network
g.get(parts[0])()
return g
def registerPlugin(name, currentValue=None, public=True):
group = registerGlobalValue(supybot.plugins, name,