mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-23 02:49:27 +01:00
Various minor refactorings, moved supybot.reply.{brackets,pipeSyntax} to supybot.commands.nested.
This commit is contained in:
parent
ab21fc54cf
commit
3e58419338
@ -120,7 +120,6 @@ class Observer(callbacks.Privmsg):
|
||||
self.commandCalled = False
|
||||
return
|
||||
channel = msg.args[0]
|
||||
Owner = irc.getCallback('Owner')
|
||||
observers = self.registryValue('observers')
|
||||
active = self.registryValue('observers.active', channel)
|
||||
for name in active:
|
||||
@ -141,7 +140,7 @@ class Observer(callbacks.Privmsg):
|
||||
for (i, group) in enumerate(groups):
|
||||
command = command.replace('$%s' % i, group)
|
||||
tokens = callbacks.tokenize(command, channel=channel)
|
||||
Owner.processTokens(irc, msg, tokens)
|
||||
self.Proxy(irc, msg, tokens)
|
||||
|
||||
def list(self, irc, msg, args):
|
||||
"""[<channel>]
|
||||
|
@ -115,7 +115,8 @@ class Misc(callbacks.Privmsg):
|
||||
# to log this in that case anyway, it being a nested command.
|
||||
self.log.info('Not replying to %s, not a command.' % tokens[0])
|
||||
if not isinstance(irc.irc, irclib.Irc):
|
||||
brackets = conf.get(conf.supybot.reply.brackets, channel)
|
||||
bracketConfig = conf.supybot.commands.nested.brackets
|
||||
brackets = conf.get(bracketConfig, channel)
|
||||
if brackets:
|
||||
(left, right) = brackets
|
||||
irc.reply(left + ' '.join(tokens) + right)
|
||||
|
@ -338,9 +338,6 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
||||
name, e)
|
||||
world.starting = False
|
||||
|
||||
def processTokens(self, irc, msg, tokens):
|
||||
callbacks.IrcObjectProxy(irc, msg, tokens)
|
||||
|
||||
def do376(self, irc, msg):
|
||||
channels = ircutils.IrcSet(conf.supybot.channels())
|
||||
channels |= conf.supybot.networks.get(irc.network).channels()
|
||||
@ -370,10 +367,9 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
||||
if ignored:
|
||||
self.log.info('Ignoring command from %s.' % msg.prefix)
|
||||
return
|
||||
brackets = conf.get(conf.supybot.reply.brackets, msg.args[0])
|
||||
try:
|
||||
tokens = callbacks.tokenize(s, brackets=brackets)
|
||||
self.processTokens(irc, msg, tokens)
|
||||
tokens = callbacks.tokenize(s, channel=msg.args[0])
|
||||
self.Proxy(irc, msg, tokens)
|
||||
except SyntaxError, e:
|
||||
irc.queueMsg(callbacks.error(msg, str(e)))
|
||||
|
||||
|
@ -230,15 +230,19 @@ class Tokenizer(object):
|
||||
# double-quote, left-bracket, and right-bracket.
|
||||
validChars = string.ascii.translate(string.ascii, '\x00\r\n \t"')
|
||||
quotes = '"'
|
||||
def __init__(self, tokens=''):
|
||||
# Add a '|' to tokens to have the pipe syntax.
|
||||
self.validChars = self.validChars.translate(string.ascii, tokens)
|
||||
if len(tokens) >= 2:
|
||||
self.left = tokens[0]
|
||||
self.right = tokens[1]
|
||||
def __init__(self, brackets='', pipe=False):
|
||||
if brackets:
|
||||
self.validChars = self.validChars.translate(string.ascii, brackets)
|
||||
self.left = brackets[0]
|
||||
self.right = brackets[1]
|
||||
else:
|
||||
self.left = ''
|
||||
self.right = ''
|
||||
self.pipe = pipe
|
||||
if self.pipe:
|
||||
self.validChars = self.validChars.translate(string.ascii, '|')
|
||||
else:
|
||||
assert '|' in self.validChars
|
||||
|
||||
def _handleToken(self, token):
|
||||
if token[0] == token[-1] and token[0] in self.quotes:
|
||||
@ -276,7 +280,10 @@ class Tokenizer(object):
|
||||
token = lexer.get_token()
|
||||
if not token:
|
||||
break
|
||||
elif token == '|' and conf.supybot.reply.pipeSyntax():
|
||||
elif token == '|' and self.pipe:
|
||||
# The "and self.pipe" might seem redundant here, but it's there
|
||||
# for strings like 'foo | bar', where a pipe stands alone as a
|
||||
# token, but shouldn't be treated specially.
|
||||
if not args:
|
||||
raise SyntaxError, '"|" with nothing preceding. I ' \
|
||||
'obviously can\'t do a pipe with ' \
|
||||
@ -303,18 +310,20 @@ class Tokenizer(object):
|
||||
args[-1].append(ends.pop())
|
||||
return args
|
||||
|
||||
def tokenize(s, brackets=None, channel=None):
|
||||
def tokenize(s, channel=None):
|
||||
"""A utility function to create a Tokenizer and tokenize a string."""
|
||||
pipe = False
|
||||
brackets = ''
|
||||
nested = conf.supybot.commands.nested
|
||||
if nested():
|
||||
brackets = conf.get(nested.brackets, channel)
|
||||
if conf.get(nested.pipeSyntax, channel): # No nesting, no pipe.
|
||||
pipe = True
|
||||
start = time.time()
|
||||
try:
|
||||
if brackets is None:
|
||||
tokens = conf.get(conf.supybot.reply.brackets, channel)
|
||||
else:
|
||||
tokens = brackets
|
||||
if conf.get(conf.supybot.reply.pipeSyntax, channel):
|
||||
tokens = '%s|' % tokens
|
||||
ret = Tokenizer(brackets=brackets, pipe=pipe).tokenize(s)
|
||||
log.stat('tokenize took %s seconds.' % (time.time() - start))
|
||||
return Tokenizer(tokens).tokenize(s)
|
||||
return ret
|
||||
except ValueError, e:
|
||||
raise SyntaxError, str(e)
|
||||
|
||||
@ -496,11 +505,21 @@ _repr = repr
|
||||
|
||||
class IrcObjectProxy(RichReplyMethods):
|
||||
"A proxy object to allow proper nested of commands (even threaded ones)."
|
||||
def __init__(self, irc, msg, args, nested=False):
|
||||
def __init__(self, irc, msg, args, nested=0):
|
||||
log.debug('IrcObjectProxy.__init__: %s' % args)
|
||||
self.irc = irc
|
||||
self.msg = msg
|
||||
self.nested = nested
|
||||
if not self.nested and isinstance(irc, self.__class__):
|
||||
# This is for plugins that indirectly spawn a Proxy, like Alias.
|
||||
self.nested += irc.nested
|
||||
maxNesting = conf.supybot.commands.nested.maximum()
|
||||
if maxNesting and self.nested > maxNesting:
|
||||
log.warning('%s attempted more than %s levels of nesting.',
|
||||
self.msg.prefix, maxNesting)
|
||||
self.error('You\'ve attempted more nesting than is currently '
|
||||
'allowed on this bot.')
|
||||
return
|
||||
# The deepcopy here is necessary for Scheduler; it re-runs already
|
||||
# tokenized commands.
|
||||
self.args = copy.deepcopy(args)
|
||||
@ -539,7 +558,7 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
self.counter += 1
|
||||
else:
|
||||
self.__class__(self, self.msg,
|
||||
self.args[self.counter], nested=True)
|
||||
self.args[self.counter], nested=self.nested+1)
|
||||
return
|
||||
self.finalEval()
|
||||
|
||||
|
42
src/conf.py
42
src/conf.py
@ -288,22 +288,6 @@ registerGlobalValue(supybot.reply, 'oneToOne',
|
||||
safety purposes (so the bot is less likely to flood) it will normally send
|
||||
everything in a single message, using mores if necessary."""))
|
||||
|
||||
class ValidBrackets(registry.OnlySomeStrings):
|
||||
validStrings = ('', '[]', '<>', '{}', '()')
|
||||
|
||||
registerChannelValue(supybot.reply, 'brackets',
|
||||
ValidBrackets('[]', """Supybot allows you to specify what brackets are used
|
||||
for your nested commands. Valid sets of brackets include [], <>, and {}
|
||||
(). [] has strong historical motivation, as well as being the brackets
|
||||
that don't require shift. <> or () might be slightly superior because they
|
||||
cannot occur in a nick. If this value is set to the empty string, no
|
||||
nesting will be allowed."""))
|
||||
|
||||
registerChannelValue(supybot.reply, 'pipeSyntax',
|
||||
registry.Boolean(False, """Supybot allows nested commands. Enabling this
|
||||
option will allow nested commands with a syntax similar to UNIX pipes, for
|
||||
example: 'bot: foo | bar'."""))
|
||||
|
||||
registerChannelValue(supybot.reply, 'whenNotCommand',
|
||||
registry.Boolean(True, """Determines whether the bot will reply with an
|
||||
error message when it is addressed but not given a valid command. If this
|
||||
@ -518,6 +502,32 @@ registerGlobalValue(supybot, 'flush',
|
||||
# supybot.commands. For stuff relating to commands.
|
||||
###
|
||||
registerGroup(supybot, 'commands')
|
||||
# This is a GlobalValue because bot owners should be able to say, "There will
|
||||
# be no nesting at all on this bot." Individual channels can just set their
|
||||
# brackets to the empty string.
|
||||
registerGlobalValue(supybot.commands, 'nested',
|
||||
registry.Boolean(True, """Determines whether the bot will allow nested
|
||||
commands, which rule. You definitely should keep this on."""))
|
||||
registerGlobalValue(supybot.commands.nested, 'maximum',
|
||||
registry.PositiveInteger(10, """Determines what the maximum number of
|
||||
nested commands will be; users will receive an error if they attempt
|
||||
commands more nested than this."""))
|
||||
|
||||
class ValidBrackets(registry.OnlySomeStrings):
|
||||
validStrings = ('', '[]', '<>', '{}', '()')
|
||||
|
||||
registerChannelValue(supybot.commands.nested, 'brackets',
|
||||
ValidBrackets('[]', """Supybot allows you to specify what brackets are used
|
||||
for your nested commands. Valid sets of brackets include [], <>, and {}
|
||||
(). [] has strong historical motivation, as well as being the brackets
|
||||
that don't require shift. <> or () might be slightly superior because they
|
||||
cannot occur in a nick. If this string is empty, nested commands will
|
||||
not be allowed in this channel."""))
|
||||
registerChannelValue(supybot.commands.nested, 'pipeSyntax',
|
||||
registry.Boolean(False, """Supybot allows nested commands. Enabling this
|
||||
option will allow nested commands with a syntax similar to UNIX pipes, for
|
||||
example: 'bot: foo | bar'."""))
|
||||
|
||||
registerGroup(supybot.commands, 'defaultPlugins',
|
||||
orderAlphabetically=True, help=utils.normalizeWhitespace("""Determines
|
||||
what commands have default plugins set, and which plugins are set to
|
||||
|
@ -79,15 +79,15 @@ class TokenizerTestCase(SupyTestCase):
|
||||
self.assertEqual(tokenize('foo bar [baz quux]'),
|
||||
['foo', 'bar', ['baz', 'quux']])
|
||||
try:
|
||||
orig = conf.supybot.reply.brackets()
|
||||
conf.supybot.reply.brackets.setValue('')
|
||||
orig = conf.supybot.commands.nested()
|
||||
conf.supybot.commands.nested.setValue(False)
|
||||
self.assertEqual(tokenize('[]'), ['[]'])
|
||||
self.assertEqual(tokenize('[foo]'), ['[foo]'])
|
||||
self.assertEqual(tokenize('foo [bar]'), ['foo', '[bar]'])
|
||||
self.assertEqual(tokenize('foo bar [baz quux]'),
|
||||
['foo', 'bar', '[baz', 'quux]'])
|
||||
finally:
|
||||
conf.supybot.reply.brackets.setValue(orig)
|
||||
conf.supybot.commands.nested.setValue(orig)
|
||||
|
||||
def testError(self):
|
||||
self.assertRaises(SyntaxError, tokenize, '[foo') #]
|
||||
@ -95,7 +95,7 @@ class TokenizerTestCase(SupyTestCase):
|
||||
|
||||
def testPipe(self):
|
||||
try:
|
||||
conf.supybot.reply.pipeSyntax.set('True')
|
||||
conf.supybot.commands.nested.pipeSyntax.setValue(True)
|
||||
self.assertRaises(SyntaxError, tokenize, '| foo')
|
||||
self.assertRaises(SyntaxError, tokenize, 'foo ||bar')
|
||||
self.assertRaises(SyntaxError, tokenize, 'bar |')
|
||||
@ -110,7 +110,7 @@ class TokenizerTestCase(SupyTestCase):
|
||||
self.assertEqual(tokenize('foo bar | baz quux'),
|
||||
['baz', 'quux', ['foo', 'bar']])
|
||||
finally:
|
||||
conf.supybot.reply.pipeSyntax.set('False')
|
||||
conf.supybot.commands.nested.pipeSyntax.setValue(False)
|
||||
self.assertEqual(tokenize('foo|bar'), ['foo|bar'])
|
||||
self.assertEqual(tokenize('foo | bar'), ['foo', '|', 'bar'])
|
||||
self.assertEqual(tokenize('foo | bar | baz'),
|
||||
|
Loading…
Reference in New Issue
Block a user