Changed the whole handling of nonCommands and ambiguousCommands and whatnot. Now plugins can define an 'invalidCommand' method to be called on invalid commands.

This commit is contained in:
Jeremy Fincher 2003-10-28 00:22:15 +00:00
parent 114909f702
commit e03c65f753
5 changed files with 76 additions and 117 deletions

View File

@ -43,6 +43,7 @@ from itertools import ifilter
import conf
import debug
import utils
import irclib
import ircmsgs
import ircutils
import privmsgs
@ -73,56 +74,12 @@ def reload(x=None):
replyWhenNotCommand = x
class Misc(callbacks.Privmsg):
def doPrivmsg(self, irc, msg):
# This exists to be able to respond to attempts to command the bot
# with a "That's not a command!" if the proper conf.variable is set.
callbacks.Privmsg.doPrivmsg(self, irc, msg)
notCommands = []
ambiguousCommands = {}
s = callbacks.addressed(irc.nick, msg)
if s:
tokens = callbacks.tokenize(s)
commands = callbacks.getCommands(tokens)
for command in commands:
command = callbacks.canonicalName(command)
cbs = callbacks.findCallbackForCommand(irc, command)
if not cbs:
notCommands.append(command)
elif len(cbs) > 1:
ambiguousCommands[command] = [cb.name() for cb in cbs]
if ambiguousCommands:
if len(ambiguousCommands) == 1: # Common case.
(command, names) = ambiguousCommands.popitem()
names.sort()
s = 'The command %r is available in the %s plugins. '\
'Please specify the plugin whose command you ' \
'wish to call by using its name as a command ' \
'before calling it.' % \
(command, utils.commaAndify(names))
else:
L = []
while ambiguousCommands:
(command, names) = ambiguousCommands.popitem()
names.sort()
L.append('The command %r is available in the %s '
'plugins' %(command,utils.commaAndify(names)))
s = '%s; please specify from which plugins to ' \
'call these commands.' % '; '.join(L)
irc.queueMsg(callbacks.reply(msg, 'Error: ' + s))
return
if conf.replyWhenNotCommand and msg.nick!=irc.nick and notCommands:
for cb in irc.callbacks:
if isinstance(cb, callbacks.PrivmsgRegexp) or \
isinstance(cb, callbacks.PrivmsgCommandAndRegexp):
for (r, _) in cb.res:
if r.search(msg.args[1]):
return
if hasattr(cb, 'addressedRes'):
for (r, _) in cb.addressedRes:
if r.search(s):
return
irc = callbacks.IrcObjectProxyRegexp(irc)
replyWhenNotCommand(irc, msg, notCommands)
def invalidCommand(self, irc, msg, tokens):
if conf.replyWhenNotCommand:
irc.error(msg, '%r is not a valid command.' % tokens[0])
else:
if not isinstance(irc.irc, irclib.Irc):
irc.reply(msg, '[%s]' % ' '.join(tokens))
def list(self, irc, msg, args):
"""[--private] [<module name>]

View File

@ -88,8 +88,12 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
def doPrivmsg(self, irc, msg):
callbacks.Privmsg.handled = False
super(self.__class__, self).doPrivmsg(irc, msg)
notCommands = []
ambiguousCommands = {}
s = callbacks.addressed(irc.nick, msg)
if s:
callbacks.IrcObjectProxy(irc, msg, callbacks.tokenize(s))
def eval(self, irc, msg, args):
"""<expression>

View File

@ -344,6 +344,35 @@ class IrcObjectProxy:
if not args:
irc.reply(msg, '[]')
else:
if isinstance(irc, irclib.Irc):
# Let's check for {ambiguous,non}Commands.
ambiguousCommands = {}
commands = getCommands(args)
for command in commands:
command = canonicalName(command)
cbs = findCallbackForCommand(irc, command)
if len(cbs) > 1:
ambiguousCommands[command] = [cb.name() for cb in cbs]
if ambiguousCommands:
if len(ambiguousCommands) == 1: # Common case.
(command, names) = ambiguousCommands.popitem()
names.sort()
s = 'The command %r is available in the %s plugins. '\
'Please specify the plugin whose command you ' \
'wish to call by using its name as a command ' \
'before calling it.' % \
(command, utils.commaAndify(names))
else:
L = []
for (command, names) in ambiguousCommands.iteritems():
names.sort()
L.append('The command %r is available in the %s '
'plugins' %
(command, utils.commaAndify(names)))
s = '%s; please specify from which plugins to ' \
'call these commands.' % '; '.join(L)
irc.queueMsg(error(msg, s))
return
self.irc = irc
self.msg = msg
self.args = args
@ -367,23 +396,31 @@ class IrcObjectProxy:
self.finalEval()
def finalEval(self):
if self.finalEvaled:
raise ValueError, 'finalEval called twice. Odd.'
assert not self.finalEvaled, 'finalEval called twice.'
self.finalEvaled = True
originalName = self.args.pop(0)
name = canonicalName(originalName)
name = canonicalName(self.args[0])
cbs = findCallbackForCommand(self, name)
if len(cbs) == 0:
self.args.insert(0, originalName)
if not isinstance(self.irc, irclib.Irc):
# If self.irc is an actual irclib.Irc, then this is the
# first command given, and should be ignored as usual.
self.reply(self.msg, '[%s]' % ' '.join(self.args))
return
elif len(cbs) > 1:
return # Misc.doPrivmsg will handle this.
for cb in self.irc.callbacks:
if isinstance(cb, PrivmsgRegexp):
for (r, _) in cb.res:
if r.search(msg.args[1]):
return
if isinstance(cb, PrivmsgCommandAndRegexp):
for (r, _) in cb.res:
if r.search(msg.args[1]):
return
for (r, _) in cb.addressedRes:
if r.search(msg.args[1]):
return
# Ok, no regexp-based things matched.
for cb in self.irc.callbacks:
if hasattr(cb, 'invalidCommand'):
cb.invalidCommand(self, self.msg, self.args)
else:
try:
assert len(cbs) == 1
del self.args[0]
cb = cbs[0]
anticap = ircdb.makeAntiCapability(name)
#debug.printf('Checking for %s' % anticap)
@ -454,22 +491,6 @@ class IrcObjectProxy:
elif self.action:
self.irc.queueMsg(ircmsgs.action(msg.args[0], s))
else:
# The size of a PRIVMSG is:
# 1 for the colon
# len(prefix)
# 1 for the space
# 7 for the PRIVMSG
# 1 for the space
# len(target)
# 1 for the space
# 1 for the colon
# len(payload)
# 2 for the \r\n
# So non-variable stuff it's 1+1+7+1+1+1+2, or 14
# We'll estimate the channel length at 30, and we'll know the
# prefix length exactly. We also might append the string
# " (more)" to the end, so that's 7 more characters.
# 512 - 51 == 461.
s = ircutils.safeArgument(s)
allowedLength = 450 - len(self.irc.prefix)
msgs = textwrap.wrap(s, allowedLength-30) # -30 is for "nick:"
@ -689,33 +710,7 @@ class Privmsg(irclib.IrcCallback):
debug.msg('%s took %s seconds' % (funcname, elapsed), 'verbose')
def doPrivmsg(self, irc, msg, rateLimit=True):
s = addressed(irc.nick, msg)
#debug.printf('Privmsg.doPrivmsg: s == %r' % s)
if s:
recipient = msg.args[0]
if ircdb.checkIgnored(msg.prefix, recipient):
debug.msg('Privmsg.doPrivmsg: ignoring %s.' % msg.prefix)
return
try:
args = tokenize(s)
except SyntaxError, e:
irc.queueMsg(reply(msg, debug.exnToString(e)))
return
if args and isinstance(args[0], str):
args[0] = canonicalName(args[0])
if self.isCommand(args[0]):
if self.handled and args[0] not in self.alwaysCall:
return
if rateLimit:
self.rateLimiter.put(msg)
msg = self.rateLimiter.get()
if msg:
if conf.replyWhenNotCommand:
for command in getCommands(args):
command = canonicalName(command)
if not findCallbackForCommand(irc, command):
return
self.Proxy(irc, msg, args)
pass
class IrcObjectProxyRegexp:
@ -871,7 +866,6 @@ class PrivmsgCommandAndRegexp(Privmsg):
if msg:
proxy = IrcObjectProxyRegexp(irc)
self.callCommand(method,proxy,msg,m,catchErrors=True)
Privmsg.doPrivmsg(self, irc, msg, rateLimit=(not fed))
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -38,7 +38,11 @@ class MiscTestCase(ChannelPluginTestCase, PluginDocumentation):
conf.replyWhenNotCommand = True
self.prefix = 'somethingElse!user@host.domain.tld'
self.assertRegexp('foo bar baz', 'not.*command')
self.assertRegexp('foo | bar | baz', 'not.*commands')
try:
conf.enablePipeSyntax = True
self.assertRegexp('foo | bar | baz', 'not.*commands')
finally:
conf.enablePipeSyntax = False
self.assertRegexp('baz [foo] [bar]', 'not.*commands')
finally:
conf.replyWhenNotCommand = False

View File

@ -84,19 +84,19 @@ class OwnerTestCase(PluginTestCase, PluginDocumentation):
def testLoad(self):
self.assertError('load Owner')
self.assertError('load owner')
self.assertNotError('load Misc')
self.assertNotError('load Admin')
self.assertNotError('list Owner')
def testReload(self):
self.assertError('reload Misc')
self.assertNotError('load Misc')
self.assertNotError('reload Misc')
self.assertError('reload Admin')
self.assertNotError('load Admin')
self.assertNotError('reload Admin')
def testUnload(self):
self.assertError('unload Misc')
self.assertNotError('load Misc')
self.assertNotError('unload Misc')
self.assertError('unload Misc')
self.assertError('unload Admin')
self.assertNotError('load Admin')
self.assertNotError('unload Admin')
self.assertError('unload Admin')
def testSetconf(self):
self.assertRegexp('setconf', 'confDir')