diff --git a/src/ircmsgs.py b/src/ircmsgs.py index 1df56cba5..ce70f2286 100644 --- a/src/ircmsgs.py +++ b/src/ircmsgs.py @@ -337,7 +337,10 @@ def nick(): ### isNick = ircutils.isNick +areNicks = ircutils.areNicks isChannel = ircutils.isChannel +areChannels = ircutils.areChannels +areReceivers = ircutils.areReceivers isUserHostmask = ircutils.isUserHostmask def pong(payload, prefix='', msg=None): @@ -545,12 +548,14 @@ def kick(channel, nick, s='', prefix='', msg=None): return IrcMsg(prefix=prefix, command='KICK', args=(channel, nick), msg=msg) -def kicks(channel, nicks, s='', prefix='', msg=None): +def kicks(channels, nicks, s='', prefix='', msg=None): """Returns a KICK to kick each of nicks from channel with the message msg. """ + if isinstance(channels, str): # Backward compatibility + channels = [channels] if conf.supybot.protocols.irc.strictRfc(): - assert isChannel(channel), repr(channel) - assert all(isNick, nicks), nicks + assert areChannels(channels), repr(channel) + assert areNicks(nicks), repr(nicks) if msg and not prefix: prefix = msg.prefix if sys.version_info[0] < 3 and isinstance(s, unicode): @@ -566,7 +571,7 @@ def kicks(channel, nicks, s='', prefix='', msg=None): def privmsg(recipient, s, prefix='', msg=None): """Returns a PRIVMSG to recipient with the message msg.""" if conf.supybot.protocols.irc.strictRfc(): - assert (isChannel(recipient) or isNick(recipient)), repr(recipient) + assert (areReceivers(recipient)), repr(recipient) assert s, 's must not be empty.' if sys.version_info[0] < 3 and isinstance(s, unicode): s = s.encode('utf8') @@ -598,7 +603,7 @@ def action(recipient, s, prefix='', msg=None): def notice(recipient, s, prefix='', msg=None): """Returns a NOTICE to recipient with the message msg.""" if conf.supybot.protocols.irc.strictRfc(): - assert (isChannel(recipient) or isNick(recipient)), repr(recipient) + assert areReceivers(recipient), repr(recipient) assert s, 'msg must not be empty.' if sys.version_info[0] < 3 and isinstance(s, unicode): s = s.encode('utf8') @@ -735,7 +740,7 @@ def who(hostmaskOrChannel, prefix='', msg=None): def whois(nick, mask='', prefix='', msg=None): """Returns a WHOIS for nick.""" if conf.supybot.protocols.irc.strictRfc(): - assert isNick(nick), repr(nick) + assert areNicks(nick), repr(nick) if msg and not prefix: prefix = msg.prefix args = (nick,) @@ -745,7 +750,7 @@ def whois(nick, mask='', prefix='', msg=None): def names(channel=None, prefix='', msg=None): if conf.supybot.protocols.irc.strictRfc(): - assert isChannel(channel) + assert areChannels(channel) if msg and not prefix: prefix = msg.prefix if channel is not None: diff --git a/src/ircutils.py b/src/ircutils.py index abb6f69f0..d9b916819 100644 --- a/src/ircutils.py +++ b/src/ircutils.py @@ -43,6 +43,7 @@ import random import string import textwrap +import functools from cStringIO import StringIO as sio import supybot.utils as utils @@ -132,6 +133,11 @@ def isNick(s, strictRfc=True, nicklen=None): not isUserHostmask(s) and \ not ' ' in s and not '!' in s +def areNicks(s, strictRfc=True, nicklen=None): + """Like 'isNick(x)' but for comma-separated list.""" + nick = functools.partial(isNick, strictRfc=strictRfc, nicklen=nicklen) + return all(map(nick, s.split(','))) + def isChannel(s, chantypes='#&+!', channellen=50): """s => bool Returns True if s is a valid IRC channel name.""" @@ -142,6 +148,20 @@ def isChannel(s, chantypes='#&+!', channellen=50): len(s) <= channellen and \ len(s.split(None, 1)) == 1 +def areChannels(s, chantypes='#&+!',channellen=50): + """Like 'isChannel(x)' but for comma-separated list.""" + chan = functools.partial(isChannel, chantypes=chantypes, + channellen=channellen) + return all(map(chan, s.split(','))) + +def areReceivers(s, strictRfc=True, nicklen=None, chantypes='#&+!', + channellen=50): + """Like 'isNick(x) or isChannel(x)' but for comma-separated list.""" + nick = functools.partial(isNick, strictRfc=strictRfc, nicklen=nicklen) + chan = functools.partial(isChannel, chantypes=chantypes, + channellen=channellen) + return all(map(lambda x:nick(x) or chan(x), s.split(','))) + _patternCache = utils.structures.CacheDict(1000) def _hostmaskPatternEqual(pattern, hostmask): try: diff --git a/test/test_ircmsgs.py b/test/test_ircmsgs.py index a2b645a6a..832c33bdf 100644 --- a/test/test_ircmsgs.py +++ b/test/test_ircmsgs.py @@ -161,6 +161,18 @@ def testUnAction(self): msg = ircmsgs.action('#foo', s) self.assertEqual(ircmsgs.unAction(msg), s) + def testPrivmsg(self): + self.assertEqual(str(ircmsgs.privmsg('foo', 'bar')), + 'PRIVMSG foo :bar\r\n') + self.assertEqual(str(ircmsgs.privmsg('foo,bar', 'baz')), + 'PRIVMSG foo,bar :baz\r\n') + + def testWhois(self): + self.assertEqual(str(ircmsgs.whois('foo')), 'WHOIS :foo\r\n') + self.assertEqual(str(ircmsgs.whois('foo,bar')), 'WHOIS :foo,bar\r\n') + self.assertRaises(AssertionError, ircmsgs.whois, '#foo') + self.assertRaises(AssertionError, ircmsgs.whois, 'foo,#foo') + def testBan(self): channel = '#osu' ban = '*!*@*.edu'