Merge pull request #1157 from jacksonmj/alias

Alias improvements
This commit is contained in:
Valentin Lorentz 2015-08-22 16:55:03 +02:00
commit 64772d42cc
3 changed files with 96 additions and 33 deletions

View File

@ -43,5 +43,7 @@ def configure(advanced):
Alias = conf.registerPlugin('Alias') Alias = conf.registerPlugin('Alias')
conf.registerGroup(Alias, 'aliases') conf.registerGroup(Alias, 'aliases')
conf.registerGroup(Alias, 'escapedaliases') conf.registerGroup(Alias, 'escapedaliases')
conf.registerGlobalValue(Alias, 'validName',
registry.String(r'^[^\x00-\x20]+$', _("""Regex which alias names must match in order to be valid""")))
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -102,9 +102,12 @@ def findBiggestAt(alias):
else: else:
return 0 return 0
def needsEscaping(alias):
return '.' in alias or '|' in alias
def escapeAlias(alias): def escapeAlias(alias):
"""Encodes [a-z0-9.]+ into [a-z][a-z0-9]. """Encodes dots and pipes
Format: a<number of escaped chars>a(<index>d)+<word without dots>.""" Format: a<number of escaped chars>a(<index>(d|p))+<word without dots or pipes>."""
prefix = '' prefix = ''
new_alias = '' new_alias = ''
prefixes = 0 prefixes = 0
@ -242,19 +245,19 @@ class Alias(callbacks.Plugin):
# XXX This should go. aliases should be a space separate list, etc. # XXX This should go. aliases should be a space separate list, etc.
group = conf.supybot.plugins.Alias.aliases group = conf.supybot.plugins.Alias.aliases
group2 = conf.supybot.plugins.Alias.escapedaliases group2 = conf.supybot.plugins.Alias.escapedaliases
prefixLen = len(registry.split('supybot.plugins.alias.aliases'))
for (name, alias) in registry._cache.items(): for (name, alias) in registry._cache.items():
name = name.lower() name = name.lower()
nameSplit = registry.split(name)
if len(nameSplit) > prefixLen+1:
continue
if name.startswith('supybot.plugins.alias.aliases.'): if name.startswith('supybot.plugins.alias.aliases.'):
name = name[len('supybot.plugins.alias.aliases.'):] name = nameSplit[-1]
if '.' in name:
continue
conf.registerGlobalValue(group, name, registry.String('', '')) conf.registerGlobalValue(group, name, registry.String('', ''))
conf.registerGlobalValue(group.get(name), 'locked', conf.registerGlobalValue(group.get(name), 'locked',
registry.Boolean(False, '')) registry.Boolean(False, ''))
elif name.startswith('supybot.plugins.alias.escapedaliases.'): elif name.startswith('supybot.plugins.alias.escapedaliases.'):
name = name[len('supybot.plugins.alias.escapedaliases.'):] name = nameSplit[-1]
if '.' in name:
continue
conf.registerGlobalValue(group2, name, conf.registerGlobalValue(group2, name,
registry.String('', '')) registry.String('', ''))
conf.registerGlobalValue(group2.get(name), conf.registerGlobalValue(group2.get(name),
@ -269,7 +272,7 @@ class Alias(callbacks.Plugin):
command = value() command = value()
locked = value.locked() locked = value.locked()
self.aliases[unescapeAlias(name)] = [command, locked, None] self.aliases[unescapeAlias(name)] = [command, locked, None]
for (alias, (command, locked, _)) in self.aliases.items(): for (alias, (command, locked, _)) in self.aliases.copy().items():
try: try:
self.addAlias(irc, alias, command, locked) self.addAlias(irc, alias, command, locked)
except Exception as e: except Exception as e:
@ -295,6 +298,38 @@ class Alias(callbacks.Plugin):
except AttributeError: except AttributeError:
return self.aliases[command[0]][2] return self.aliases[command[0]][2]
def aliasRegistryGroup(self, name):
if needsEscaping(name):
return self.registryValue('escapedaliases', value=False)
else:
return self.registryValue('aliases', value=False)
def aliasRegistryNode(self, name):
group = self.aliasRegistryGroup(name)
if needsEscaping(name):
return group.get(escapeAlias(name))
else:
return group.get(name)
def aliasRegistryRemove(self, name):
group = self.aliasRegistryGroup(name)
if needsEscaping(name):
group.unregister(escapeAlias(name))
else:
group.unregister(name)
def setLocked(self, name, value):
self.aliases[name][1] = value
self.aliasRegistryNode(name).locked.setValue(value)
def isValidName(self, name):
if not re.search(self.registryValue('validName'), name):
return False
if not registry.isValidRegistryName(name):
return False
return True
@internationalizeDocstring @internationalizeDocstring
def lock(self, irc, msg, args, name): def lock(self, irc, msg, args, name):
"""<alias> """<alias>
@ -302,8 +337,7 @@ class Alias(callbacks.Plugin):
Locks an alias so that no one else can change it. Locks an alias so that no one else can change it.
""" """
if name in self.aliases and self.isCommandMethod(name): if name in self.aliases and self.isCommandMethod(name):
self.aliases[name][1] = True self.setLocked(name, True)
conf.supybot.plugins.Alias.aliases.get(name).locked.setValue(True)
irc.replySuccess() irc.replySuccess()
else: else:
irc.error(_('There is no such alias.')) irc.error(_('There is no such alias.'))
@ -316,20 +350,15 @@ class Alias(callbacks.Plugin):
Unlocks an alias so that people can define new aliases over it. Unlocks an alias so that people can define new aliases over it.
""" """
if name in self.aliases and self.isCommandMethod(name): if name in self.aliases and self.isCommandMethod(name):
self.aliases[name][1] = False self.setLocked(name, False)
conf.supybot.plugins.Alias.aliases.get(name).locked.setValue(False)
irc.replySuccess() irc.replySuccess()
else: else:
irc.error(_('There is no such alias.')) irc.error(_('There is no such alias.'))
unlock = wrap(unlock, [('checkCapability', 'admin'), 'commandName']) unlock = wrap(unlock, [('checkCapability', 'admin'), 'commandName'])
_validNameRe = re.compile(r'^[a-z.|!?][a-z0-9.|!]*$')
def addAlias(self, irc, name, alias, lock=False): def addAlias(self, irc, name, alias, lock=False):
if not self._validNameRe.search(name): if not self.isValidName(name):
raise AliasError('Names can only contain alphanumerical ' raise AliasError('Invalid alias name.')
'characters, dots, pipes, and '
'exclamation/interrogatin marks '
'(and the first character cannot be a number).')
realName = callbacks.canonicalName(name) realName = callbacks.canonicalName(name)
if name != realName: if name != realName:
s = format(_('That name isn\'t valid. Try %q instead.'), realName) s = format(_('That name isn\'t valid. Try %q instead.'), realName)
@ -345,15 +374,14 @@ class Alias(callbacks.Plugin):
raise AliasError(format('Alias %q is locked.', name)) raise AliasError(format('Alias %q is locked.', name))
f = makeNewAlias(name, alias) f = makeNewAlias(name, alias)
f = types.MethodType(f, self) f = types.MethodType(f, self)
if '.' in name or '|' in name:
aliasGroup = self.registryValue('escapedaliases', value=False)
confname = escapeAlias(name)
else:
aliasGroup = self.registryValue('aliases', value=False)
confname = name
if name in self.aliases: if name in self.aliases:
# We gotta remove it so its value gets updated. # We gotta remove it so its value gets updated.
aliasGroup.unregister(confname) self.aliasRegistryRemove(name)
aliasGroup = self.aliasRegistryGroup(name)
if needsEscaping(name):
confname = escapeAlias(name)
else:
confname = name
conf.registerGlobalValue(aliasGroup, confname, conf.registerGlobalValue(aliasGroup, confname,
registry.String(alias, '')) registry.String(alias, ''))
conf.registerGlobalValue(aliasGroup.get(confname), 'locked', conf.registerGlobalValue(aliasGroup.get(confname), 'locked',
@ -365,11 +393,7 @@ class Alias(callbacks.Plugin):
if name in self.aliases and self.isCommandMethod(name): if name in self.aliases and self.isCommandMethod(name):
if evenIfLocked or not self.aliases[name][1]: if evenIfLocked or not self.aliases[name][1]:
del self.aliases[name] del self.aliases[name]
if '.' in name or '|' in name: self.aliasRegistryRemove(name)
conf.supybot.plugins.Alias.escapedaliases.unregister(
escapeAlias(name))
else:
conf.supybot.plugins.Alias.aliases.unregister(name)
else: else:
raise AliasError('That alias is locked.') raise AliasError('That alias is locked.')
else: else:
@ -412,6 +436,36 @@ class Alias(callbacks.Plugin):
irc.error(str(e)) irc.error(str(e))
remove = wrap(remove, ['commandName']) remove = wrap(remove, ['commandName'])
@internationalizeDocstring
def list(self, irc, msg, args, optlist):
"""[--locked|--unlocked]
Lists alias names of a particular type, defaults to all aliases if no
--locked or --unlocked option is given.
"""
optlist = dict(optlist)
if len(optlist)>1:
irc.error(_('Cannot specify --locked and --unlocked simultaneously'))
return
aliases = []
for name in self.aliases.keys():
if self.isCommandMethod(name):
if 'locked' in optlist:
if self.aliases[name][1]: aliases.append(name)
elif 'unlocked' in optlist:
if not self.aliases[name][1]: aliases.append(name)
else:
aliases.append(name)
if aliases:
aliases.sort()
irc.reply(format('%L', aliases))
else:
if len(optlist):
irc.reply(_('There are no aliases of that type.'))
else:
irc.reply(_('There are no aliases.'))
list = wrap(list, [getopts({'locked':'', 'unlocked':''})])
Class = Alias Class = Alias

View File

@ -33,6 +33,7 @@ from supybot.test import *
import supybot.conf as conf import supybot.conf as conf
import supybot.plugin as plugin import supybot.plugin as plugin
import supybot.registry as registry import supybot.registry as registry
from supybot.utils.minisix import u
from . import plugin as Alias from . import plugin as Alias
@ -112,8 +113,7 @@ class AliasTestCase(ChannelPluginTestCase):
self.failIf('foobar' in cb.aliases) self.failIf('foobar' in cb.aliases)
self.assertError('foobar') self.assertError('foobar')
self.assertRegexp('alias add café ignore', 'Error.*can only contain') self.assertRegexp('alias add abc\x07 ignore', 'Error.*Invalid')
self.assertRegexp('alias add 1abc ignore', 'Error.*can only contain')
def testOptionalArgs(self): def testOptionalArgs(self):
self.assertNotError('alias add myrepr "repr @1"') self.assertNotError('alias add myrepr "repr @1"')
@ -128,6 +128,13 @@ class AliasTestCase(ChannelPluginTestCase):
self.assertNotError('alias add myre "echo s/$1/$2/g"') self.assertNotError('alias add myre "echo s/$1/$2/g"')
self.assertResponse('myre foo bar', 's/foo/bar/g') self.assertResponse('myre foo bar', 's/foo/bar/g')
def testUnicode(self):
self.assertNotError(u('alias add \u200b echo foo'))
self.assertResponse(u('\u200b'), 'foo')
self.assertNotError('alias add café echo bar')
self.assertResponse('café', 'bar')
def testSimpleAliasWithoutArgsImpliesDollarStar(self): def testSimpleAliasWithoutArgsImpliesDollarStar(self):
self.assertNotError('alias add exo echo') self.assertNotError('alias add exo echo')
self.assertResponse('exo foo bar baz', 'foo bar baz') self.assertResponse('exo foo bar baz', 'foo bar baz')