mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-23 11:09:23 +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
|
self.commandCalled = False
|
||||||
return
|
return
|
||||||
channel = msg.args[0]
|
channel = msg.args[0]
|
||||||
Owner = irc.getCallback('Owner')
|
|
||||||
observers = self.registryValue('observers')
|
observers = self.registryValue('observers')
|
||||||
active = self.registryValue('observers.active', channel)
|
active = self.registryValue('observers.active', channel)
|
||||||
for name in active:
|
for name in active:
|
||||||
@ -141,7 +140,7 @@ class Observer(callbacks.Privmsg):
|
|||||||
for (i, group) in enumerate(groups):
|
for (i, group) in enumerate(groups):
|
||||||
command = command.replace('$%s' % i, group)
|
command = command.replace('$%s' % i, group)
|
||||||
tokens = callbacks.tokenize(command, channel=channel)
|
tokens = callbacks.tokenize(command, channel=channel)
|
||||||
Owner.processTokens(irc, msg, tokens)
|
self.Proxy(irc, msg, tokens)
|
||||||
|
|
||||||
def list(self, irc, msg, args):
|
def list(self, irc, msg, args):
|
||||||
"""[<channel>]
|
"""[<channel>]
|
||||||
|
@ -115,7 +115,8 @@ class Misc(callbacks.Privmsg):
|
|||||||
# to log this in that case anyway, it being a nested command.
|
# to log this in that case anyway, it being a nested command.
|
||||||
self.log.info('Not replying to %s, not a command.' % tokens[0])
|
self.log.info('Not replying to %s, not a command.' % tokens[0])
|
||||||
if not isinstance(irc.irc, irclib.Irc):
|
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:
|
if brackets:
|
||||||
(left, right) = brackets
|
(left, right) = brackets
|
||||||
irc.reply(left + ' '.join(tokens) + right)
|
irc.reply(left + ' '.join(tokens) + right)
|
||||||
|
@ -338,9 +338,6 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
name, e)
|
name, e)
|
||||||
world.starting = False
|
world.starting = False
|
||||||
|
|
||||||
def processTokens(self, irc, msg, tokens):
|
|
||||||
callbacks.IrcObjectProxy(irc, msg, tokens)
|
|
||||||
|
|
||||||
def do376(self, irc, msg):
|
def do376(self, irc, msg):
|
||||||
channels = ircutils.IrcSet(conf.supybot.channels())
|
channels = ircutils.IrcSet(conf.supybot.channels())
|
||||||
channels |= conf.supybot.networks.get(irc.network).channels()
|
channels |= conf.supybot.networks.get(irc.network).channels()
|
||||||
@ -370,10 +367,9 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
|||||||
if ignored:
|
if ignored:
|
||||||
self.log.info('Ignoring command from %s.' % msg.prefix)
|
self.log.info('Ignoring command from %s.' % msg.prefix)
|
||||||
return
|
return
|
||||||
brackets = conf.get(conf.supybot.reply.brackets, msg.args[0])
|
|
||||||
try:
|
try:
|
||||||
tokens = callbacks.tokenize(s, brackets=brackets)
|
tokens = callbacks.tokenize(s, channel=msg.args[0])
|
||||||
self.processTokens(irc, msg, tokens)
|
self.Proxy(irc, msg, tokens)
|
||||||
except SyntaxError, e:
|
except SyntaxError, e:
|
||||||
irc.queueMsg(callbacks.error(msg, str(e)))
|
irc.queueMsg(callbacks.error(msg, str(e)))
|
||||||
|
|
||||||
|
@ -230,15 +230,19 @@ class Tokenizer(object):
|
|||||||
# double-quote, left-bracket, and right-bracket.
|
# double-quote, left-bracket, and right-bracket.
|
||||||
validChars = string.ascii.translate(string.ascii, '\x00\r\n \t"')
|
validChars = string.ascii.translate(string.ascii, '\x00\r\n \t"')
|
||||||
quotes = '"'
|
quotes = '"'
|
||||||
def __init__(self, tokens=''):
|
def __init__(self, brackets='', pipe=False):
|
||||||
# Add a '|' to tokens to have the pipe syntax.
|
if brackets:
|
||||||
self.validChars = self.validChars.translate(string.ascii, tokens)
|
self.validChars = self.validChars.translate(string.ascii, brackets)
|
||||||
if len(tokens) >= 2:
|
self.left = brackets[0]
|
||||||
self.left = tokens[0]
|
self.right = brackets[1]
|
||||||
self.right = tokens[1]
|
|
||||||
else:
|
else:
|
||||||
self.left = ''
|
self.left = ''
|
||||||
self.right = ''
|
self.right = ''
|
||||||
|
self.pipe = pipe
|
||||||
|
if self.pipe:
|
||||||
|
self.validChars = self.validChars.translate(string.ascii, '|')
|
||||||
|
else:
|
||||||
|
assert '|' in self.validChars
|
||||||
|
|
||||||
def _handleToken(self, token):
|
def _handleToken(self, token):
|
||||||
if token[0] == token[-1] and token[0] in self.quotes:
|
if token[0] == token[-1] and token[0] in self.quotes:
|
||||||
@ -276,7 +280,10 @@ class Tokenizer(object):
|
|||||||
token = lexer.get_token()
|
token = lexer.get_token()
|
||||||
if not token:
|
if not token:
|
||||||
break
|
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:
|
if not args:
|
||||||
raise SyntaxError, '"|" with nothing preceding. I ' \
|
raise SyntaxError, '"|" with nothing preceding. I ' \
|
||||||
'obviously can\'t do a pipe with ' \
|
'obviously can\'t do a pipe with ' \
|
||||||
@ -303,18 +310,20 @@ class Tokenizer(object):
|
|||||||
args[-1].append(ends.pop())
|
args[-1].append(ends.pop())
|
||||||
return args
|
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."""
|
"""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()
|
start = time.time()
|
||||||
try:
|
try:
|
||||||
if brackets is None:
|
ret = Tokenizer(brackets=brackets, pipe=pipe).tokenize(s)
|
||||||
tokens = conf.get(conf.supybot.reply.brackets, channel)
|
|
||||||
else:
|
|
||||||
tokens = brackets
|
|
||||||
if conf.get(conf.supybot.reply.pipeSyntax, channel):
|
|
||||||
tokens = '%s|' % tokens
|
|
||||||
log.stat('tokenize took %s seconds.' % (time.time() - start))
|
log.stat('tokenize took %s seconds.' % (time.time() - start))
|
||||||
return Tokenizer(tokens).tokenize(s)
|
return ret
|
||||||
except ValueError, e:
|
except ValueError, e:
|
||||||
raise SyntaxError, str(e)
|
raise SyntaxError, str(e)
|
||||||
|
|
||||||
@ -496,11 +505,21 @@ _repr = repr
|
|||||||
|
|
||||||
class IrcObjectProxy(RichReplyMethods):
|
class IrcObjectProxy(RichReplyMethods):
|
||||||
"A proxy object to allow proper nested of commands (even threaded ones)."
|
"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)
|
log.debug('IrcObjectProxy.__init__: %s' % args)
|
||||||
self.irc = irc
|
self.irc = irc
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
self.nested = nested
|
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
|
# The deepcopy here is necessary for Scheduler; it re-runs already
|
||||||
# tokenized commands.
|
# tokenized commands.
|
||||||
self.args = copy.deepcopy(args)
|
self.args = copy.deepcopy(args)
|
||||||
@ -539,7 +558,7 @@ class IrcObjectProxy(RichReplyMethods):
|
|||||||
self.counter += 1
|
self.counter += 1
|
||||||
else:
|
else:
|
||||||
self.__class__(self, self.msg,
|
self.__class__(self, self.msg,
|
||||||
self.args[self.counter], nested=True)
|
self.args[self.counter], nested=self.nested+1)
|
||||||
return
|
return
|
||||||
self.finalEval()
|
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
|
safety purposes (so the bot is less likely to flood) it will normally send
|
||||||
everything in a single message, using mores if necessary."""))
|
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',
|
registerChannelValue(supybot.reply, 'whenNotCommand',
|
||||||
registry.Boolean(True, """Determines whether the bot will reply with an
|
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
|
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.
|
# supybot.commands. For stuff relating to commands.
|
||||||
###
|
###
|
||||||
registerGroup(supybot, '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',
|
registerGroup(supybot.commands, 'defaultPlugins',
|
||||||
orderAlphabetically=True, help=utils.normalizeWhitespace("""Determines
|
orderAlphabetically=True, help=utils.normalizeWhitespace("""Determines
|
||||||
what commands have default plugins set, and which plugins are set to
|
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]'),
|
self.assertEqual(tokenize('foo bar [baz quux]'),
|
||||||
['foo', 'bar', ['baz', 'quux']])
|
['foo', 'bar', ['baz', 'quux']])
|
||||||
try:
|
try:
|
||||||
orig = conf.supybot.reply.brackets()
|
orig = conf.supybot.commands.nested()
|
||||||
conf.supybot.reply.brackets.setValue('')
|
conf.supybot.commands.nested.setValue(False)
|
||||||
self.assertEqual(tokenize('[]'), ['[]'])
|
self.assertEqual(tokenize('[]'), ['[]'])
|
||||||
self.assertEqual(tokenize('[foo]'), ['[foo]'])
|
self.assertEqual(tokenize('[foo]'), ['[foo]'])
|
||||||
self.assertEqual(tokenize('foo [bar]'), ['foo', '[bar]'])
|
self.assertEqual(tokenize('foo [bar]'), ['foo', '[bar]'])
|
||||||
self.assertEqual(tokenize('foo bar [baz quux]'),
|
self.assertEqual(tokenize('foo bar [baz quux]'),
|
||||||
['foo', 'bar', '[baz', 'quux]'])
|
['foo', 'bar', '[baz', 'quux]'])
|
||||||
finally:
|
finally:
|
||||||
conf.supybot.reply.brackets.setValue(orig)
|
conf.supybot.commands.nested.setValue(orig)
|
||||||
|
|
||||||
def testError(self):
|
def testError(self):
|
||||||
self.assertRaises(SyntaxError, tokenize, '[foo') #]
|
self.assertRaises(SyntaxError, tokenize, '[foo') #]
|
||||||
@ -95,7 +95,7 @@ class TokenizerTestCase(SupyTestCase):
|
|||||||
|
|
||||||
def testPipe(self):
|
def testPipe(self):
|
||||||
try:
|
try:
|
||||||
conf.supybot.reply.pipeSyntax.set('True')
|
conf.supybot.commands.nested.pipeSyntax.setValue(True)
|
||||||
self.assertRaises(SyntaxError, tokenize, '| foo')
|
self.assertRaises(SyntaxError, tokenize, '| foo')
|
||||||
self.assertRaises(SyntaxError, tokenize, 'foo ||bar')
|
self.assertRaises(SyntaxError, tokenize, 'foo ||bar')
|
||||||
self.assertRaises(SyntaxError, tokenize, 'bar |')
|
self.assertRaises(SyntaxError, tokenize, 'bar |')
|
||||||
@ -110,7 +110,7 @@ class TokenizerTestCase(SupyTestCase):
|
|||||||
self.assertEqual(tokenize('foo bar | baz quux'),
|
self.assertEqual(tokenize('foo bar | baz quux'),
|
||||||
['baz', 'quux', ['foo', 'bar']])
|
['baz', 'quux', ['foo', 'bar']])
|
||||||
finally:
|
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'), ['foo', '|', 'bar'])
|
self.assertEqual(tokenize('foo | bar'), ['foo', '|', 'bar'])
|
||||||
self.assertEqual(tokenize('foo | bar | baz'),
|
self.assertEqual(tokenize('foo | bar | baz'),
|
||||||
|
Loading…
Reference in New Issue
Block a user