Merge remote-tracking branch 'supybot/master' into testing

Conflicts:
	.mailmap
	README
	docs/FAQ.rst
	plugins/Ctcp/plugin.py
	plugins/Misc/plugin.py
	plugins/Network/plugin.py
	plugins/QuoteGrabs/plugin.py
	plugins/RSS/README.txt
	plugins/Relay/plugin.py
	plugins/ShrinkUrl/config.py
	plugins/ShrinkUrl/plugin.py
	plugins/ShrinkUrl/test.py
	setup.py
	src/callbacks.py
	src/commands.py
	src/conf.py
	test/test_commands.py
This commit is contained in:
Valentin Lorentz 2015-05-15 14:41:08 +02:00
commit 487f8c8af5
10 changed files with 163 additions and 162 deletions

View File

@ -56,8 +56,8 @@ class Admin(callbacks.Plugin):
def do437(self, irc, msg):
"""Nick/channel temporarily unavailable."""
target = msg.args[0]
t = time.time() + 30
if irc.isChannel(target): # We don't care about nicks.
t = time.time() + 30
# Let's schedule a rejoin.
networkGroup = conf.supybot.networks.get(irc.network)
def rejoin():
@ -67,6 +67,16 @@ class Admin(callbacks.Plugin):
schedule.addEvent(rejoin, t)
self.log.info('Scheduling a rejoin to %s at %s; '
'Channel temporarily unavailable.', target, t)
else:
irc = self.pendingNickChanges.get(irc, None)
if irc is not None:
def nick():
irc.queueMsg(ircmsgs.nick(target))
schedule.addEvent(nick, t)
self.log.info('Scheduling a nick change to %s at %s; '
'Nick temporarily unavailable.', target, t)
else:
self.log.debug('Got 437 without Admin.nick being called.')
def do471(self, irc, msg):
try:

View File

@ -1,6 +1,6 @@
###
# Copyright (c) 2002-2004, Jeremiah Fincher
# Copyright (c) 2010, James McCoy
# Copyright (c) 2010,2015 James McCoy
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -185,99 +185,10 @@ class Network(callbacks.Plugin):
if (irc, loweredNick) not in self._whois:
return
(replyIrc, replyMsg, d, command) = self._whois[(irc, loweredNick)]
START_CODE = '311' if command == 'whois' else '314'
hostmask = '@'.join(d[START_CODE].args[2:4])
user = d[START_CODE].args[-1]
if '319' in d:
channels = []
for msg in d['319']:
channels.extend(msg.args[-1].split())
ops = []
voices = []
normal = []
halfops = []
for channel in channels:
origchan = channel
channel = channel.lstrip('@%+~!')
# UnrealIRCd uses & for user modes and disallows it as a
# channel-prefix, flying in the face of the RFC. Have to
# handle this specially when processing WHOIS response.
testchan = channel.lstrip('&')
if testchan != channel and irc.isChannel(testchan):
channel = testchan
diff = len(channel) - len(origchan)
modes = origchan[:diff]
chan = irc.state.channels.get(channel)
# The user is in a channel the bot is in, so the ircd may have
# responded with otherwise private data.
if chan:
# Skip channels the caller isn't in. This prevents
# us from leaking information when the channel is +s or the
# target is +i.
if replyMsg.nick not in chan.users:
continue
# Skip +s channels the target is in only if the reply isn't
# being sent to that channel.
if 's' in chan.modes and \
not ircutils.strEqual(replyMsg.args[0], channel):
continue
if not modes:
normal.append(channel)
elif utils.iter.any(lambda c: c in modes,('@', '&', '~', '!')):
ops.append(channel)
elif utils.iter.any(lambda c: c in modes, ('%',)):
halfops.append(channel)
elif utils.iter.any(lambda c: c in modes, ('+',)):
voices.append(channel)
L = []
if ops:
L.append(format(_('is an op on %L'), ops))
if halfops:
L.append(format(_('is a halfop on %L'), halfops))
if voices:
L.append(format(_('is voiced on %L'), voices))
if normal:
if L:
L.append(format(_('is also on %L'), normal))
else:
L.append(format(_('is on %L'), normal))
else:
if command == 'whois':
L = [_('isn\'t on any publicly visible channels')]
else:
L = []
channels = format('%L', L)
if '317' in d:
idle = utils.timeElapsed(d['317'].args[2])
signon = time.strftime(conf.supybot.reply.format.time(),
time.localtime(float(d['317'].args[3])))
else:
idle = _('<unknown>')
signon = _('<unknown>')
if '312' in d:
server = d['312'].args[2]
if len(d['312']) > 3:
signoff = d['312'].args[3]
else:
server = _('<unknown>')
if '301' in d:
away = ' %s is away: %s.' % (nick, d['301'].args[2])
else:
away = ''
if '320' in d:
if d['320'].args[2]:
identify = _(' identified')
else:
identify = ''
else:
identify = ''
if command == 'whois':
s = _('%s (%s) has been%s on server %s since %s (idle for %s). %s '
'%s.%s') % (user, hostmask, identify, server,
signon, idle, nick, channels, away)
else:
s = _('%s (%s) has been%s on server %s and disconnected on %s.') % \
(user, hostmask, identify, server, signoff)
d['318'] = msg
s = ircutils.formatWhois(irc, d, caller=replyMsg.nick,
channel=replyMsg.args[0],
command=command)
replyIrc.reply(s)
del self._whois[(irc, loweredNick)]
do369 = do318

View File

@ -279,7 +279,7 @@ class QuoteGrabs(callbacks.Plugin):
# opposed to channel which is used to determine which db to store the
# quote in
chan = msg.args[0]
if chan is None or not ircutils.isChannel(chan):
if chan is None or not irc.isChannel(chan):
raise callbacks.ArgumentError
if ircutils.nickEqual(nick, msg.nick):
irc.error(_('You can\'t quote grab yourself.'), Raise=True)

View File

@ -1,6 +1,6 @@
###
# Copyright (c) 2002-2004, Jeremiah Fincher
# Copyright (c) 2010, James McCoy
# Copyright (c) 2010,2015 James McCoy
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -202,64 +202,9 @@ class Relay(callbacks.Plugin):
if (irc, loweredNick) not in self._whois:
return
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
hostmask = '@'.join(d['311'].args[2:4])
user = d['311'].args[-1]
if '319' in d:
channels = d['319'].args[-1].split()
ops = []
voices = []
normal = []
halfops = []
for channel in channels:
if channel.startswith('@'):
ops.append(channel[1:])
elif channel.startswith('%'):
halfops.append(channel[1:])
elif channel.startswith('+'):
voices.append(channel[1:])
else:
normal.append(channel)
L = []
if ops:
L.append(format(_('is an op on %L'), ops))
if halfops:
L.append(format(_('is a halfop on %L'), halfops))
if voices:
L.append(format(_('is voiced on %L'), voices))
if normal:
if L:
L.append(format(_('is also on %L'), normal))
else:
L.append(format(_('is on %L'), normal))
else:
L = [_('isn\'t on any non-secret channels')]
channels = format('%L', L)
if '317' in d:
idle = utils.timeElapsed(d['317'].args[2])
signon = time.strftime(conf.supybot.reply.format.time(),
time.localtime(float(d['317'].args[3])))
else:
idle = _('<unknown>')
signon = _('<unknown>')
if '312' in d:
server = d['312'].args[2]
else:
server = _('<unknown>')
if '301' in d:
away = format(_(' %s is away: %s.'), nick, d['301'].args[2])
else:
away = ''
if '320' in d:
if d['320'].args[2]:
identify = _(' identified')
else:
identify = ''
else:
identify = ''
s = format(_('%s (%s) has been%s on server %s since %s (idle for %s) '
'and %s.%s'),
user, hostmask, identify, server, signon, idle,
channels, away)
d['318'] = msg
s = ircutils.formatWhois(irc, d, caller=replyMsg.nick,
channel=replyMsg.args[0])
replyIrc.reply(s)
del self._whois[(irc, loweredNick)]

View File

@ -71,7 +71,7 @@ conf.registerChannelValue(ShrinkUrl, 'shrinkSnarfer',
shrink snarfer is enabled. This snarfer will watch for URLs in the
channel, and if they're sufficiently long (as determined by
supybot.plugins.ShrinkUrl.minimumLength) it will post a
smaller URL from tinyurl.com, as denoted in
smaller URL from the service as denoted in
supybot.plugins.ShrinkUrl.default.""")))
conf.registerChannelValue(ShrinkUrl.shrinkSnarfer, 'showDomain',
registry.Boolean(True, _("""Determines whether the snarfer will show the

View File

@ -1289,7 +1289,7 @@ class Commands(BasePlugin):
if 'command has no help.' in help:
# Note: this case will never happen, unless 'checkDoc' is set
# to False.
irc.error(_('Invalid arguments for %s.') % ' '.join(command))
irc.error(_('Invalid arguments for %s.') % formatCommand(command))
else:
irc.reply(help)
except (SyntaxError, Error) as e:

View File

@ -1,6 +1,6 @@
###
# Copyright (c) 2002-2005, Jeremiah Fincher
# Copyright (c) 2009-2010, James McCoy
# Copyright (c) 2009-2010,2015, James McCoy
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -443,8 +443,8 @@ def _getRe(f):
else:
state.args.append(s)
else:
state.errorInvalid(_('regular expression'), s)
except IndexError:
raise ValueError
except (ValueError, IndexError):
args[:] = original
state.errorInvalid(_('regular expression'), s)
return get

View File

@ -1135,8 +1135,11 @@ class Irc(IrcCommandDispatcher):
if not self.afterConnect:
newNick = self._getNextNick()
assert newNick != self.nick
log.info('Got 433: %s %s. Trying %s.',self.nick, problem, newNick)
log.info('Got %s: %s %s. Trying %s.',
msg.command, self.nick, problem, newNick)
self.sendMsg(ircmsgs.nick(newNick))
def do437(self, msg):
self.do43x(msg, 'is temporarily unavailable')
def do433(self, msg):
self.do43x(msg, 'is in use')
def do432(self, msg):

View File

@ -1,6 +1,6 @@
###
# Copyright (c) 2002-2005, Jeremiah Fincher
# Copyright (c) 2009,2011, James McCoy
# Copyright (c) 2009,2011,2015 James McCoy
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -375,6 +375,123 @@ def stripFormatting(s):
s = stripUnderline(s)
return s.replace('\x0f', '').replace('\x0F', '')
_containsFormattingRe = re.compile(r'[\x02\x03\x16\x1f]')
def formatWhois(irc, replies, caller='', channel='', command='whois'):
"""Returns a string describing the target of a WHOIS command.
Arguments are:
* irc: the irclib.Irc object on which the replies was received
* replies: a dict mapping the reply codes ('311', '312', etc.) to their
corresponding ircmsg.IrcMsg
* caller: an optional nick specifying who requested the whois information
* channel: an optional channel specifying where the reply will be sent
If provided, caller and channel will be used to avoid leaking information
that the caller/channel shouldn't be privy to.
"""
hostmask = '@'.join(replies['311'].args[2:4])
nick = replies['318'].args[1]
user = replies['311'].args[-1]
(replyIrc, replyMsg, d, command) = self._whois[(irc, loweredNick)]
START_CODE = '311' if command == 'whois' else '314'
hostmask = '@'.join(d[START_CODE].args[2:4])
user = d[START_CODE].args[-1]
if _containsFormattingRe.search(user) and user[-1] != '\x0f':
# For good measure, disable any formatting
user = '%s\x0f' % user
if '319' in replies:
channels = replies['319'].args[-1].split()
ops = []
voices = []
normal = []
halfops = []
for chan in channels:
origchan = chan
chan = chan.lstrip('@%+~!')
# UnrealIRCd uses & for user modes and disallows it as a
# channel-prefix, flying in the face of the RFC. Have to
# handle this specially when processing WHOIS response.
testchan = chan.lstrip('&')
if testchan != chan and irc.isChannel(testchan):
chan = testchan
diff = len(chan) - len(origchan)
modes = origchan[:diff]
chanState = irc.state.channels.get(chan)
# The user is in a channel the bot is in, so the ircd may have
# responded with otherwise private data.
if chanState:
# Skip channels the callee isn't in. This helps prevents
# us leaking information when the channel is +s or the
# target is +i
if caller not in chanState.users:
continue
# Skip +s channels the target is in only if the reply isn't
# being sent to that channel
if 's' in chanState.modes and \
not ircutils.strEqual(channel or '', chan):
continue
if not modes:
normal.append(chan)
elif utils.iter.any(lambda c: c in modes,('@', '&', '~', '!')):
ops.append(chan[1:])
elif utils.iter.any(lambda c: c in modes, ('%',)):
halfops.append(chan[1:])
elif utils.iter.any(lambda c: c in modes, ('+',)):
voices.append(chan[1:])
L = []
if ops:
L.append(format(_('is an op on %L'), ops))
if halfops:
L.append(format(_('is a halfop on %L'), halfops))
if voices:
L.append(format(_('is voiced on %L'), voices))
if normal:
if L:
L.append(format(_('is also on %L'), normal))
else:
L.append(format(_('is on %L'), normal))
else:
if command == 'whois':
L = [_('isn\'t on any non-secret channels')]
else:
L = []
channels = format('%L', L)
if '317' in replies:
idle = utils.timeElapsed(replies['317'].args[2])
signon = utils.str.timestamp(float(replies['317'].args[3]))
else:
idle = '<unknown>'
signon = '<unknown>'
if '312' in replies:
server = replies['312'].args[2]
if len(d['312']) > 3:
signoff = d['312'].args[3]
else:
server = '<unknown>'
if '301' in replies:
away = ' %s is away: %s.' % (nick, replies['301'].args[2])
else:
away = ''
if '320' in replies:
if replies['320'].args[2]:
identify = ' identified'
else:
identify = ''
else:
identify = ''
if command == 'whois':
s = _('%s (%s) has been%s on server %s since %s (idle for %s) and '
'%s.%s').decode('utf8') % (user, hostmask, identify, server,
signon, idle, channels, away)
else:
s = _('%s (%s) has been%s on server %s and disconnect on %s.') \
.decode('utf8') % \
(user, hostmask, identify, server, signoff)
return s
class FormatContext(object):
def __init__(self):
self.reset()

View File

@ -1,5 +1,6 @@
###
# Copyright (c) 2002-2005, Jeremiah Fincher
# Copyright (c) 2015, James McCoy
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -196,13 +197,24 @@ class FirstTestCase(CommandsTestCase):
self.assertStateErrored([first('int', 'something')], ['words'],
errored=False)
<<<<<<< HEAD
=======
def testLongRegexp(self):
spec = [first('regexpMatcher', 'regexpReplacer'), 'text']
self.assertStateErrored(spec, ['s/foo/bar/', 'x' * 512], errored=False)
>>>>>>> supybot/master
class GetoptTestCase(PluginTestCase):
plugins = ('Misc',) # We put something so it does not complain
class Foo(callbacks.Plugin):
def bar(self, irc, msg, args, optlist):
irc.reply(' '.join(sorted(['%s:%d'%x for x in optlist])))
<<<<<<< HEAD
bar = wrap(bar, [getopts({'foo': 'int', 'fbar': 'int'})],
checkDoc=False)
=======
bar = wrap(bar, [getopts({'foo': 'int', 'fbar': 'int'})])
>>>>>>> supybot/master
def testGetoptsExact(self):
self.irc.addCallback(self.Foo(self.irc))
@ -211,6 +223,9 @@ class GetoptTestCase(PluginTestCase):
self.assertResponse('bar --f 3 --fb 5',
'Error: Invalid arguments for bar.')
<<<<<<< HEAD
=======
>>>>>>> supybot/master
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: