diff --git a/plugins/Owner/plugin.py b/plugins/Owner/plugin.py index 07dc88a5d..3326f9ea5 100644 --- a/plugins/Owner/plugin.py +++ b/plugins/Owner/plugin.py @@ -261,7 +261,7 @@ class Owner(callbacks.Plugin): tokens = callbacks.tokenize(s, channel=msg.args[0]) self.Proxy(irc, msg, tokens) except SyntaxError as e: - irc.queueMsg(callbacks.error(msg, str(e))) + irc.error(str(e)) def logmark(self, irc, msg, args, text): """ diff --git a/src/callbacks.py b/src/callbacks.py index 6d1676e97..219da6468 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -156,17 +156,25 @@ def canonicalName(command, preserve_spaces=False): command = command[:-1] return ''.join([x for x in command if x not in special]).lower() + reAppend -def reply(msg, s, prefixNick=None, private=None, - notice=None, to=None, action=None, error=False, - stripCtcp=True): +def reply(*args, **kwargs): + warnings.warn('callbacks.reply is deprecated. Use irc.reply instead.', + DeprecationWarning) + return _makeReply(dynamic.irc, *args, **kwargs) + +def _makeReply(irc, msg, s, + prefixNick=None, private=None, + notice=None, to=None, action=None, error=False, + stripCtcp=True): msg.tag('repliedTo') # Ok, let's make the target: # XXX This isn't entirely right. Consider to=#foo, private=True. target = ircutils.replyTo(msg) - if ircutils.isChannel(to): + def isPublic(s): + return irc.isChannel(irc._stripChannelPrefix(s)) + if to is not None and isPublic(to): target = to - if ircutils.isChannel(target): - channel = target + if isPublic(target): + channel = irc._stripChannelPrefix(target) else: channel = None if notice is None: @@ -195,11 +203,11 @@ def reply(msg, s, prefixNick=None, private=None, s = ircutils.safeArgument(s) if not s and not action: s = _('Error: I tried to send you an empty message.') - if prefixNick and ircutils.isChannel(target): + if prefixNick and isPublic(target): # Let's may sure we don't do, "#channel: foo.". - if not ircutils.isChannel(to): + if not isPublic(to): s = '%s: %s' % (to, s) - if not ircutils.isChannel(target): + if not isPublic(target): if conf.supybot.reply.withNoticeWhenPrivate(): notice = True # And now, let's decide whether it's a PRIVMSG or a NOTICE. @@ -214,11 +222,16 @@ def reply(msg, s, prefixNick=None, private=None, ret.tag('inReplyTo', msg) return ret -def error(msg, s, **kwargs): +def error(*args, **kwargs): + warnings.warn('callbacks.error is deprecated. Use irc.error instead.', + DeprecationWarning) + return _makeErrorReply(dynamic.irc, *args, **kwargs) + +def _makeErrorReply(irc, msg, s, **kwargs): """Makes an error reply to msg with the appropriate error payload.""" kwargs['error'] = True msg.tag('isError') - return reply(msg, s, **kwargs) + return _makeReply(irc, msg, s, **kwargs) def getHelp(method, name=None, doc=None): if name is None: @@ -400,8 +413,8 @@ def checkCommandCapability(msg, cb, commandName): checkCapability(antiCommand) checkAtEnd = [commandName] default = conf.supybot.capabilities.default() - if ircutils.isChannel(msg.args[0]): - channel = msg.args[0] + if msg.channel: + channel = msg.channel checkCapability(ircdb.makeChannelCapability(channel, antiCommand)) chanCommand = ircdb.makeChannelCapability(channel, commandName) checkAtEnd += [chanCommand] @@ -426,7 +439,7 @@ class RichReplyMethods(object): return ircutils.standardSubstitute(self, self.msg, s) def _getConfig(self, wrapper): - return conf.get(wrapper, self.msg.args[0]) + return conf.get(wrapper, self.msg.channel) def replySuccess(self, s='', **kwargs): v = self._getConfig(conf.supybot.replies.success) @@ -599,7 +612,7 @@ class ReplyIrcProxy(RichReplyMethods): raise ArgumentError if msg is None: msg = self.msg - m = error(msg, s, **kwargs) + m = _makeErrorReply(self, msg, s, **kwargs) self.irc.queueMsg(m) return m @@ -609,7 +622,7 @@ class ReplyIrcProxy(RichReplyMethods): assert not isinstance(s, ircmsgs.IrcMsg), \ 'Old code alert: there is no longer a "msg" argument to reply.' kwargs.pop('noLengthCheck', None) - m = reply(msg, s, **kwargs) + m = _makeReply(self, msg, s, **kwargs) self.irc.queueMsg(m) return m @@ -665,9 +678,9 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): self.notice = None self.private = None self.noLengthCheck = None - if self.irc.isChannel(self.msg.args[0]): + if self.msg.channel: self.prefixNick = conf.get(conf.supybot.reply.withNickPrefix, - self.msg.args[0]) + self.msg.channel) else: self.prefixNick = conf.supybot.reply.withNickPrefix() @@ -894,12 +907,12 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): elif self.noLengthCheck: # noLengthCheck only matters to NestedCommandsIrcProxy, so # it's not used here. Just in case you were wondering. - m = reply(msg, s, to=self.to, - notice=self.notice, - action=self.action, - private=self.private, - prefixNick=self.prefixNick, - stripCtcp=stripCtcp) + m = _makeReply(self, msg, s, to=self.to, + notice=self.notice, + action=self.action, + private=self.private, + prefixNick=self.prefixNick, + stripCtcp=stripCtcp) sendMsg(m) return m else: @@ -930,11 +943,11 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): # action implies noLengthCheck, which has already been # handled. Let's stick an assert in here just in case. assert not self.action - m = reply(msg, s, to=self.to, - notice=self.notice, - private=self.private, - prefixNick=self.prefixNick, - stripCtcp=stripCtcp) + m = _makeReply(self, msg, s, to=self.to, + notice=self.notice, + private=self.private, + prefixNick=self.prefixNick, + stripCtcp=stripCtcp) sendMsg(m) return m # The '(XX more messages)' may have not the same @@ -946,11 +959,11 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): while instant > 1 and msgs: instant -= 1 response = msgs.pop() - m = reply(msg, response, to=self.to, - notice=self.notice, - private=self.private, - prefixNick=self.prefixNick, - stripCtcp=stripCtcp) + m = _makeReply(self, msg, response, to=self.to, + notice=self.notice, + private=self.private, + prefixNick=self.prefixNick, + stripCtcp=stripCtcp) sendMsg(m) # XXX We should somehow allow these to be returned, but # until someone complains, we'll be fine :) We @@ -976,15 +989,15 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): pass # We'll leave it as it is. mask = prefix.split('!', 1)[1] self._mores[mask] = msgs - public = self.irc.isChannel(msg.args[0]) + public = bool(self.msg.channel) private = self.private or not public self._mores[msg.nick] = (private, msgs) - m = reply(msg, response, to=self.to, - action=self.action, - notice=self.notice, - private=self.private, - prefixNick=self.prefixNick, - stripCtcp=stripCtcp) + m = _makeReply(self, msg, response, to=self.to, + action=self.action, + notice=self.notice, + private=self.private, + prefixNick=self.prefixNick, + stripCtcp=stripCtcp) sendMsg(m) return m finally: @@ -1035,7 +1048,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): if not isinstance(self.irc, irclib.Irc): return self.irc.error(s, **kwargs) else: - m = error(self.msg, s, **kwargs) + m = _makeErrorReply(self, self.msg, s, **kwargs) self.irc.queueMsg(m) return m else: @@ -1346,7 +1359,7 @@ class Commands(BasePlugin, SynchronizedAndFirewalled): help = getHelp chan = None if dynamic.msg is not None: - chan = dynamic.msg.args[0] + chan = dynamic.msg.channel if simpleSyntax is None: simpleSyntax = conf.get(conf.supybot.reply.showSimpleSyntax, chan) if simpleSyntax: @@ -1385,7 +1398,7 @@ class PluginMixin(BasePlugin, irclib.IrcCallback): else: noIgnore = self.noIgnore if noIgnore or \ - not ircdb.checkIgnored(msg.prefix, msg.args[0]) or \ + not ircdb.checkIgnored(msg.prefix, msg.channel) or \ not ircutils.isUserHostmask(msg.prefix): # Some services impl. self.__parent.__call__(irc, msg) else: diff --git a/src/commands.py b/src/commands.py index dada7b9d5..f06ab92de 100644 --- a/src/commands.py +++ b/src/commands.py @@ -205,9 +205,12 @@ def urlSnarfer(f): """Protects the snarfer from loops (with other bots) and whatnot.""" def newf(self, irc, msg, match, *L, **kwargs): url = match.group(0) - channel = msg.args[0] - if not irc.isChannel(channel) or (ircmsgs.isCtcp(msg) and not - ircmsgs.isAction(msg)): + channel = msg.channel + if not channel: + # Don't snarf in private + return + if not (ircmsgs.isCtcp(msg) and not ircmsgs.isAction(msg)): + # Don't snarf CTCPs unless they are a /me return if ircdb.channels.getChannel(channel).lobotomized: self.log.debug('Not snarfing in %s: lobotomized.', channel) @@ -491,10 +494,11 @@ def getChannel(irc, msg, args, state): return if args and irc.isChannel(args[0]): channel = args.pop(0) - elif irc.isChannel(msg.args[0]): - channel = msg.args[0] + elif msg.channel: + channel = msg.channel else: state.log.debug('Raising ArgumentError because there is no channel.') + print(msg.channel, msg) raise callbacks.ArgumentError state.channel = channel state.args.append(channel) @@ -502,8 +506,8 @@ def getChannel(irc, msg, args, state): def getChannels(irc, msg, args, state): if args and all(map(irc.isChannel, args[0].split(','))): channels = args.pop(0).split(',') - elif irc.isChannel(msg.args[0]): - channels = [msg.args[0]] + elif msg.channel: + channels = [msg.channel] else: state.log.debug('Raising ArgumentError because there is no channel.') raise callbacks.ArgumentError @@ -535,11 +539,11 @@ def inChannel(irc, msg, args, state): state.error(_('I\'m not in %s.') % state.channel, Raise=True) def onlyInChannel(irc, msg, args, state): - if not (irc.isChannel(msg.args[0]) and msg.args[0] in irc.state.channels): + if not (msg.channel and msg.channel in irc.state.channels): state.error(_('This command may only be given in a channel that I am ' 'in.'), Raise=True) else: - state.channel = msg.args[0] + state.channel = msg.channel state.args.append(state.channel) def callerInGivenChannel(irc, msg, args, state): @@ -576,8 +580,8 @@ def getChannelOrGlobal(irc, msg, args, state): elif args and irc.isChannel(args[0]): channel = args.pop(0) state.channel = channel - elif irc.isChannel(msg.args[0]): - channel = msg.args[0] + elif msg.channel: + channel = msg.channel state.channel = channel else: state.log.debug('Raising ArgumentError because there is no channel.') @@ -620,11 +624,11 @@ def getSomethingNoSpaces(irc, msg, args, state, *L): getSomething(irc, msg, args, state, p=p, *L) def private(irc, msg, args, state): - if irc.isChannel(msg.args[0]): + if msg.channel: state.errorRequiresPrivacy(Raise=True) def public(irc, msg, args, state, errmsg=None): - if not irc.isChannel(msg.args[0]): + if not msg.channel: if errmsg is None: errmsg = _('This message must be sent in a channel.') state.error(errmsg, Raise=True) diff --git a/src/irclib.py b/src/irclib.py index 280bfefac..c82552836 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -875,25 +875,33 @@ class Irc(IrcCommandDispatcher, log.Firewalled): else: return None - _numericErrorCommandRe = re.compile(r'^[45][0-9][0-9]$') - def feedMsg(self, msg): - """Called by the IrcDriver; feeds a message received.""" + def _tagMsg(self, msg): + """Sets attribute on an incoming IRC message. Will usually only be + called by feedMsg, but may be useful in tests as well.""" msg.tag('receivedBy', self) msg.tag('receivedOn', self.network) msg.tag('receivedAt', time.time()) # Check if the message is sent to a channel if msg.args: - channel = msg.args[0] + msg.channel = msg.args[0] if msg.command in ('NOTICE', 'PRIVMSG') and \ not conf.supybot.protocols.irc.strictRfc(): - statusmsg_chars = self.state.supported.get('statusmsg', '') - channel = channel.lstrip(statusmsg_chars) - if not self.isChannel(channel): - channel = None + msg.channel = self._stripChannelPrefix(msg.channel) + if not self.isChannel(msg.channel): + msg.channel = None else: - channel = None - msg.channel = channel + msg.channel = None + + def _stripChannelPrefix(self, channel): + statusmsg_chars = self.state.supported.get('statusmsg', '') + return channel.lstrip(statusmsg_chars) + + _numericErrorCommandRe = re.compile(r'^[45][0-9][0-9]$') + def feedMsg(self, msg): + """Called by the IrcDriver; feeds a message received.""" + self._tagMsg(msg) + channel = msg.channel preInFilter = str(msg).rstrip('\r\n') log.debug('Incoming message (%s): %s', self.network, preInFilter) diff --git a/src/ircutils.py b/src/ircutils.py index 8a87e66fc..435e0b34a 100644 --- a/src/ircutils.py +++ b/src/ircutils.py @@ -659,7 +659,10 @@ def safeArgument(s): def replyTo(msg): """Returns the appropriate target to send responses to msg.""" - if isChannel(msg.args[0]): + if msg.channel: + # if message was sent to +#channel, we want to reply to +#channel; + # or unvoiced channel users will see the bot reply without the + # origin query return msg.args[0] else: return msg.nick @@ -867,10 +870,7 @@ def standardSubstitute(irc, msg, text, env=None): vars.update(msg.reply_env) if irc and msg: - if isChannel(msg.args[0]): - channel = msg.args[0] - else: - channel = 'somewhere' + channel = msg.channel or 'somewhere' def randNick(): if channel != 'somewhere': L = list(irc.state.channels[channel].users) diff --git a/test/test_callbacks.py b/test/test_callbacks.py index 07334b609..f22d8dff9 100644 --- a/test/test_callbacks.py +++ b/test/test_callbacks.py @@ -175,6 +175,7 @@ class FunctionsTestCase(SupyTestCase): self.assertEqual('foobar--', callbacks.canonicalName('foobar--')) def testAddressed(self): + irc = getTestIrc() oldprefixchars = str(conf.supybot.reply.whenAddressedBy.chars) nick = 'supybot' conf.supybot.reply.whenAddressedBy.chars.set('~!@') @@ -189,6 +190,7 @@ class FunctionsTestCase(SupyTestCase): for msg in inChannel: self.assertEqual('foo', callbacks.addressed(nick, msg), msg) msg = ircmsgs.privmsg(nick, 'foo') + irc._tagMsg(msg) self.assertEqual('foo', callbacks.addressed(nick, msg)) conf.supybot.reply.whenAddressedBy.chars.set(oldprefixchars) msg = ircmsgs.privmsg('#foo', '%s::::: bar' % nick) @@ -216,52 +218,84 @@ class FunctionsTestCase(SupyTestCase): conf.supybot.reply.whenNotAddressed.setValue(original) def testAddressedWithMultipleNicks(self): + irc = getTestIrc() msg = ircmsgs.privmsg('#foo', 'bar: baz') + irc._tagMsg(msg) self.assertEqual(callbacks.addressed('bar', msg), 'baz') # need to recreate the msg objects since the old ones have already # been tagged msg = ircmsgs.privmsg('#foo', 'bar: baz') + irc._tagMsg(msg) self.assertEqual(callbacks.addressed('biff', msg, nicks=['bar']), 'baz') def testAddressedWithNickAtEnd(self): + irc = getTestIrc() msg = ircmsgs.privmsg('#foo', 'baz, bar') + irc._tagMsg(msg) self.assertEqual(callbacks.addressed('bar', msg, whenAddressedByNickAtEnd=True), 'baz') def testAddressedPrefixCharsTakePrecedenceOverNickAtEnd(self): + irc = getTestIrc() msg = ircmsgs.privmsg('#foo', '@echo foo') + irc._tagMsg(msg) self.assertEqual(callbacks.addressed('foo', msg, whenAddressedByNickAtEnd=True, prefixChars='@'), 'echo foo') - def testReply(self): + irc = getTestIrc() prefix = 'foo!bar@baz' channelMsg = ircmsgs.privmsg('#foo', 'bar baz', prefix=prefix) nonChannelMsg = ircmsgs.privmsg('supybot', 'bar baz', prefix=prefix) + irc._tagMsg(channelMsg) + irc._tagMsg(nonChannelMsg) self.assertEqual(ircmsgs.notice(nonChannelMsg.nick, 'foo'), - callbacks.reply(channelMsg, 'foo', private=True)) + callbacks._makeReply(irc, channelMsg, 'foo', + private=True)) self.assertEqual(ircmsgs.notice(nonChannelMsg.nick, 'foo'), - callbacks.reply(nonChannelMsg, 'foo')) + callbacks._makeReply(irc, nonChannelMsg, 'foo')) self.assertEqual(ircmsgs.privmsg(channelMsg.args[0], '%s: foo' % channelMsg.nick), - callbacks.reply(channelMsg, 'foo')) + callbacks._makeReply(irc, channelMsg, 'foo')) self.assertEqual(ircmsgs.privmsg(channelMsg.args[0], 'foo'), - callbacks.reply(channelMsg, 'foo', prefixNick=False)) + callbacks._makeReply(irc, channelMsg, 'foo', + prefixNick=False)) self.assertEqual(ircmsgs.notice(nonChannelMsg.nick, 'foo'), - callbacks.reply(channelMsg, 'foo', - notice=True, private=True)) + callbacks._makeReply(irc, channelMsg, 'foo', + notice=True, private=True)) + + def testReplyStatusmsg(self): + irc = getTestIrc() + prefix = 'foo!bar@baz' + msg = ircmsgs.privmsg('+#foo', 'bar baz', prefix=prefix) + irc._tagMsg(msg) + + # No statusmsg set, so understood as being a private message, so + # private reply + self.assertEqual(ircmsgs.notice(msg.nick, 'foo'), + callbacks._makeReply(irc, msg, 'foo')) + + irc.state.supported['statusmsg'] = '+' + msg = ircmsgs.privmsg('+#foo', 'bar baz', prefix=prefix) + irc._tagMsg(msg) + print(msg.channel) + self.assertEqual(ircmsgs.privmsg('+#foo', '%s: foo' % msg.nick), + callbacks._makeReply(irc, msg, 'foo')) def testReplyTo(self): + irc = getTestIrc() prefix = 'foo!bar@baz' msg = ircmsgs.privmsg('#foo', 'bar baz', prefix=prefix) - self.assertEqual(callbacks.reply(msg, 'blah', to='blah'), + irc._tagMsg(msg) + self.assertEqual(callbacks._makeReply(irc, msg, 'blah', to='blah'), ircmsgs.privmsg('#foo', 'blah: blah')) - self.assertEqual(callbacks.reply(msg, 'blah', to='blah', private=True), + self.assertEqual(callbacks._makeReply(irc, msg, 'blah', to='blah', + private=True), ircmsgs.notice('blah', 'blah')) def testTokenize(self): @@ -682,7 +716,9 @@ class WithPrivateNoticeTestCase(ChannelPluginTestCase): class ProxyTestCase(SupyTestCase): def testHashing(self): + irc = getTestIrc() msg = ircmsgs.ping('0') + irc._tagMsg(msg) irc = irclib.Irc('test') proxy = callbacks.SimpleProxy(irc, msg) # First one way... diff --git a/test/test_commands.py b/test/test_commands.py index 54eead35e..4b35fd598 100644 --- a/test/test_commands.py +++ b/test/test_commands.py @@ -47,6 +47,7 @@ class CommandsTestCase(SupyTestCase): realIrc = getTestIrc() realIrc.nick = 'test' realIrc.state.supported['chantypes'] = '#' + realIrc._tagMsg(msg) irc = callbacks.SimpleProxy(realIrc, msg) myspec = Spec(spec, **kwargs) state = myspec(irc, msg, given) diff --git a/test/test_ircutils.py b/test/test_ircutils.py index 8d77629e1..71d647780 100644 --- a/test/test_ircutils.py +++ b/test/test_ircutils.py @@ -281,12 +281,10 @@ class FunctionsTestCase(SupyTestCase): def testStandardSubstitute(self): # Stub out random msg and irc objects that provide what # standardSubstitute wants - msg = ircmsgs.IrcMsg(':%s PRIVMSG #channel :stuff' % self.hostmask) - class Irc(object): - nick = 'bob' - network = 'testnet' + irc = getTestIrc() - irc = Irc() + msg = ircmsgs.IrcMsg(':%s PRIVMSG #channel :stuff' % self.hostmask) + irc._tagMsg(msg) f = ircutils.standardSubstitute vars = {'foo': 'bar', 'b': 'c', 'i': 100, @@ -344,9 +342,12 @@ class FunctionsTestCase(SupyTestCase): self.assertEqual('{}|^', ircutils.toLower('[]\\~')) def testReplyTo(self): + irc = getTestIrc() prefix = 'foo!bar@baz' channel = ircmsgs.privmsg('#foo', 'bar baz', prefix=prefix) private = ircmsgs.privmsg('jemfinch', 'bar baz', prefix=prefix) + irc._tagMsg(channel) + irc._tagMsg(private) self.assertEqual(ircutils.replyTo(channel), channel.args[0]) self.assertEqual(ircutils.replyTo(private), private.nick) diff --git a/test/test_standardSubstitute.py b/test/test_standardSubstitute.py index d108d7b53..f39d4ae3e 100644 --- a/test/test_standardSubstitute.py +++ b/test/test_standardSubstitute.py @@ -37,54 +37,53 @@ class holder: users = set(map(str, range(1000))) class FunctionsTestCase(SupyTestCase): - class irc: - class state: - channels = {'#foo': holder()} - nick = 'foobar' - network = 'testnet' @retry() def testStandardSubstitute(self): + irc = getTestIrc() + irc.state.channels = {'#foo': holder()} + f = ircutils.standardSubstitute msg = ircmsgs.privmsg('#foo', 'filler', prefix='biff!quux@xyzzy') - s = f(self.irc, msg, '$rand') + irc._tagMsg(msg) + s = f(irc, msg, '$rand') try: int(s) except ValueError: self.fail('$rand wasn\'t an int.') - s = f(self.irc, msg, '$randomInt') + s = f(irc, msg, '$randomInt') try: int(s) except ValueError: self.fail('$randomint wasn\'t an int.') - self.assertEqual(f(self.irc, msg, '$botnick'), self.irc.nick) - self.assertEqual(f(self.irc, msg, '$who'), msg.nick) - self.assertEqual(f(self.irc, msg, '$WHO'), + self.assertEqual(f(irc, msg, '$botnick'), irc.nick) + self.assertEqual(f(irc, msg, '$who'), msg.nick) + self.assertEqual(f(irc, msg, '$WHO'), msg.nick, 'stand. sub. not case-insensitive.') - self.assertEqual(f(self.irc, msg, '$nick'), msg.nick) - self.assertNotEqual(f(self.irc, msg, '$randomdate'), '$randomdate') - q = f(self.irc,msg,'$randomdate\t$randomdate') + self.assertEqual(f(irc, msg, '$nick'), msg.nick) + self.assertNotEqual(f(irc, msg, '$randomdate'), '$randomdate') + q = f(irc,msg,'$randomdate\t$randomdate') dl = q.split('\t') if dl[0] == dl[1]: self.fail ('Two $randomdates in the same string were the same') - q = f(self.irc, msg, '$randomint\t$randomint') + q = f(irc, msg, '$randomint\t$randomint') dl = q.split('\t') if dl[0] == dl[1]: self.fail ('Two $randomints in the same string were the same') - self.assertNotEqual(f(self.irc, msg, '$today'), '$today') - self.assertNotEqual(f(self.irc, msg, '$now'), '$now') - n = f(self.irc, msg, '$randnick') - self.failUnless(n in self.irc.state.channels['#foo'].users) - n = f(self.irc, msg, '$randomnick') - self.failUnless(n in self.irc.state.channels['#foo'].users) - n = f(self.irc, msg, '$randomnick '*100) + self.assertNotEqual(f(irc, msg, '$today'), '$today') + self.assertNotEqual(f(irc, msg, '$now'), '$now') + n = f(irc, msg, '$randnick') + self.failUnless(n in irc.state.channels['#foo'].users) + n = f(irc, msg, '$randomnick') + self.failUnless(n in irc.state.channels['#foo'].users) + n = f(irc, msg, '$randomnick '*100) L = n.split() self.failIf(all(L[0].__eq__, L), 'all $randomnicks were the same') - c = f(self.irc, msg, '$channel') + c = f(irc, msg, '$channel') self.assertEqual(c, msg.args[0]) - net = f(self.irc, msg, '$network') - self.assertEqual(net, self.irc.network) + net = f(irc, msg, '$network') + self.assertEqual(net, irc.network)