Compare commits

..

5 Commits

13 changed files with 126 additions and 32 deletions

View File

@ -13,7 +13,7 @@ Usage
----- -----
This plugin constantly tries to take whatever nick is configured as This plugin constantly tries to take whatever nick is configured as
supybot.nick. Just make sure that's set appropriately, and thus plugin supybot.nick. Just make sure that's set appropriately, and this plugin
will do the rest. will do the rest.
.. _conf-NickCapture: .. _conf-NickCapture:

View File

@ -31,7 +31,7 @@ msgstr ""
#: plugin.py:41 #: plugin.py:41
msgid "" msgid ""
"This plugin constantly tries to take whatever nick is configured as\n" "This plugin constantly tries to take whatever nick is configured as\n"
" supybot.nick. Just make sure that's set appropriately, and thus plugin\n" " supybot.nick. Just make sure that's set appropriately, and this plugin\n"
" will do the rest." " will do the rest."
msgstr "" msgstr ""
"Dieses Plugin versucht dauernd den Nick der in supybot.nick konfiguriert ist " "Dieses Plugin versucht dauernd den Nick der in supybot.nick konfiguriert ist "

View File

@ -34,7 +34,7 @@ msgstr ""
#: plugin.py:41 #: plugin.py:41
msgid "" msgid ""
"This plugin constantly tries to take whatever nick is configured as\n" "This plugin constantly tries to take whatever nick is configured as\n"
" supybot.nick. Just make sure that's set appropriately, and thus plugin\n" " supybot.nick. Just make sure that's set appropriately, and this plugin\n"
" will do the rest." " will do the rest."
msgstr "" msgstr ""
"Tämä lisäosa yrittää jatkuvasti ottaa sen nimimerkin, joka on määritetty\n" "Tämä lisäosa yrittää jatkuvasti ottaa sen nimimerkin, joka on määritetty\n"

View File

@ -32,7 +32,7 @@ msgstr ""
#: plugin.py:41 #: plugin.py:41
msgid "" msgid ""
"This plugin constantly tries to take whatever nick is configured as\n" "This plugin constantly tries to take whatever nick is configured as\n"
" supybot.nick. Just make sure that's set appropriately, and thus plugin\n" " supybot.nick. Just make sure that's set appropriately, and this plugin\n"
" will do the rest." " will do the rest."
msgstr "" msgstr ""
"Ce plugin essaye constament de récupérer le nick configuré dans supybot." "Ce plugin essaye constament de récupérer le nick configuré dans supybot."

View File

@ -28,7 +28,7 @@ msgstr ""
#: plugin.py:41 #: plugin.py:41
msgid "" msgid ""
"This plugin constantly tries to take whatever nick is configured as\n" "This plugin constantly tries to take whatever nick is configured as\n"
" supybot.nick. Just make sure that's set appropriately, and thus plugin\n" " supybot.nick. Just make sure that's set appropriately, and this plugin\n"
" will do the rest." " will do the rest."
msgstr "" msgstr ""
"Questo plugin cerca costantemente di ottenere qualsiasi nick sia impostato\n" "Questo plugin cerca costantemente di ottenere qualsiasi nick sia impostato\n"

View File

@ -31,7 +31,7 @@ msgstr ""
#, docstring #, docstring
msgid "" msgid ""
"This plugin constantly tries to take whatever nick is configured as\n" "This plugin constantly tries to take whatever nick is configured as\n"
" supybot.nick. Just make sure that's set appropriately, and thus plugin\n" " supybot.nick. Just make sure that's set appropriately, and this plugin\n"
" will do the rest." " will do the rest."
msgstr "" msgstr ""

View File

@ -39,7 +39,7 @@ _ = PluginInternationalization('NickCapture')
class NickCapture(callbacks.Plugin): class NickCapture(callbacks.Plugin):
"""This plugin constantly tries to take whatever nick is configured as """This plugin constantly tries to take whatever nick is configured as
supybot.nick. Just make sure that's set appropriately, and thus plugin supybot.nick. Just make sure that's set appropriately, and this plugin
will do the rest.""" will do the rest."""
public = False public = False
def __init__(self, irc): def __init__(self, irc):

View File

@ -195,6 +195,11 @@ class Seen(callbacks.Plugin):
if len(results) == 1: if len(results) == 1:
(nick, info) = results[0] (nick, info) = results[0]
(when, said) = info (when, said) = info
if nick in irc.state.channels[channel].users:
reply = format(_('%s was last seen in %s %s ago, and is in the channel now'),
nick, channel,
utils.timeElapsed(time.time()-when))
else:
reply = format(_('%s was last seen in %s %s ago'), reply = format(_('%s was last seen in %s %s ago'),
nick, channel, nick, channel,
utils.timeElapsed(time.time()-when)) utils.timeElapsed(time.time()-when))
@ -207,12 +212,19 @@ class Seen(callbacks.Plugin):
L = [] L = []
for (nick, info) in results: for (nick, info) in results:
(when, said) = info (when, said) = info
if nick in irc.state.channels[channel].users:
L.append(format(_('%s (%s ago, and is in the channel now)'), nick,
utils.timeElapsed(time.time()-when)))
else:
L.append(format(_('%s (%s ago)'), nick, L.append(format(_('%s (%s ago)'), nick,
utils.timeElapsed(time.time()-when))) utils.timeElapsed(time.time()-when)))
irc.reply(format(_('%s could be %L'), name, (L, _('or')))) irc.reply(format(_('%s could be %L'), name, (L, _('or'))))
else: else:
irc.reply(format(_('I haven\'t seen anyone matching %s.'), name)) irc.reply(format(_('I haven\'t seen anyone matching %s.'), name))
except KeyError: except KeyError:
if name in irc.state.channels[channel].users:
irc.reply(format(_("%s is in the channel right now."), name))
else:
irc.reply(format(_('I have not seen %s.'), name)) irc.reply(format(_('I have not seen %s.'), name))
def _checkChannelPresence(self, irc, channel, target, you): def _checkChannelPresence(self, irc, channel, target, you):
@ -277,8 +289,13 @@ class Seen(callbacks.Plugin):
db = self.db db = self.db
try: try:
(when, said) = db.seen(channel, '<last>') (when, said) = db.seen(channel, '<last>')
reply = format(_('Someone was last seen in %s %s ago'), pattern = r'<(.*?)>'
channel, utils.timeElapsed(time.time()-when)) match = re.search(pattern, said)
if not match:
irc.error(format(_('I couldn\'t parse the nick of the speaker of the last line.')), Raise=True)
nick = match.group(1)
reply = format(_('Last seen in %s was %s, %s ago'),
channel, nick, utils.timeElapsed(time.time()-when))
if self.registryValue('showLastMessage', channel, irc.network): if self.registryValue('showLastMessage', channel, irc.network):
reply = _('%s: %s') % (reply, said) reply = _('%s: %s') % (reply, said)
irc.reply(reply) irc.reply(reply)
@ -303,6 +320,11 @@ class Seen(callbacks.Plugin):
db = self.db db = self.db
try: try:
(when, said) = db.seen(channel, user.id) (when, said) = db.seen(channel, user.id)
if user.name in irc.state.channels[channel].users:
reply = format(_('%s was last seen in %s %s ago and is in the channel now'),
user.name, channel,
utils.timeElapsed(time.time()-when))
else:
reply = format(_('%s was last seen in %s %s ago'), reply = format(_('%s was last seen in %s %s ago'),
user.name, channel, user.name, channel,
utils.timeElapsed(time.time()-when)) utils.timeElapsed(time.time()-when))
@ -310,6 +332,9 @@ class Seen(callbacks.Plugin):
reply = _('%s: %s') % (reply, said) reply = _('%s: %s') % (reply, said)
irc.reply(reply) irc.reply(reply)
except KeyError: except KeyError:
if user.name in irc.state.channels[channel].users:
irc.reply(format(_("%s is in the channel right now."), user.name))
else:
irc.reply(format(_('I have not seen %s.'), user.name)) irc.reply(format(_('I have not seen %s.'), user.name))
@internationalizeDocstring @internationalizeDocstring

View File

@ -83,12 +83,10 @@ class ChannelDBTestCase(ChannelPluginTestCase):
self.assertNotError('seen last') self.assertNotError('seen last')
self.assertNotError('list') self.assertNotError('list')
self.assertNotError('config plugins.Seen.minimumNonWildcard 2') self.assertNotError('config plugins.Seen.minimumNonWildcard 2')
self.assertError('seen *')
self.assertNotError('seen %s' % self.nick)
m = self.assertNotError('seen %s' % self.nick.upper())
self.assertIn(self.nick.upper(), m.args[1])
self.assertRegexp('seen user %s' % self.nick, self.assertRegexp('seen user %s' % self.nick,
'^%s was last seen' % self.nick) '^%s was last seen' % self.nick)
self.assertError('seen *')
self.assertNotError('seen %s' % self.nick)
self.assertNotError('config plugins.Seen.minimumNonWildcard 0') self.assertNotError('config plugins.Seen.minimumNonWildcard 0')
orig = conf.supybot.protocols.irc.strictRfc() orig = conf.supybot.protocols.irc.strictRfc()
try: try:
@ -101,6 +99,28 @@ class ChannelDBTestCase(ChannelPluginTestCase):
finally: finally:
conf.supybot.protocols.irc.strictRfc.setValue(orig) conf.supybot.protocols.irc.strictRfc.setValue(orig)
def testSeenNickInChannel(self):
# Test case: 'seen' with a nick (user in channel)
self.irc.feedMsg(ircmsgs.join(self.channel, self.irc.nick,
prefix=self.prefix))
self.assertRegexp('seen %s' % self.nick, 'is in the channel right now')
m = self.assertNotError('seen %s' % self.nick.upper())
self.assertIn(self.nick.upper(), m.args[1])
def testSeenUserInChannel(self):
# Test case: 'seen' with a user (user in channel)
self.irc.feedMsg(ircmsgs.join(self.channel, self.irc.nick,
prefix=self.prefix))
self.assertRegexp('seen user %s' % self.nick, 'is in the channel right now')
def testSeenNickNotInChannel(self):
# Test case: 'seen' with a nick (user not in channel)
testnick = "user123"
self.irc.feedMsg(ircmsgs.join(self.channel, testnick, "user123!baz"))
self.irc.feedMsg(ircmsgs.part(self.channel, prefix="user123!baz"))
self.assertNotRegexp("seen %s" % testnick, "is in the channel right now")
def testSeenNoUser(self): def testSeenNoUser(self):
self.irc.feedMsg(ircmsgs.join(self.channel, self.irc.nick, self.irc.feedMsg(ircmsgs.join(self.channel, self.irc.nick,
prefix=self.prefix)) prefix=self.prefix))

View File

@ -76,15 +76,51 @@ class Status(callbacks.Plugin):
Returns the status of the bot. Returns the status of the bot.
""" """
# Initialize dictionaries
nicks = {}
networks = {} networks = {}
# Iterate through each IRC network
for Irc in world.ircs: for Irc in world.ircs:
networks.setdefault(Irc.network, []).append(Irc.nick) network_name = Irc.network
networks = sorted(networks.items()) channels = Irc.state.channels
networks = [format(_('%s as %L'), net, nicks) for (net,nicks) in networks]
L = [format(_('I am connected to %L.'), networks)] # Initialize counts for this network
channel_counts = len(channels)
op_counts = sum(1 for channel in channels.values() if Irc.nick in channel.ops)
halfop_counts = sum(1 for channel in channels.values() if Irc.nick in channel.halfops)
voice_counts = sum(1 for channel in channels.values() if Irc.nick in channel.voices)
normal_counts = sum(1 for channel in channels.values() if Irc.nick in channel.users)
# Store the counts in dictionaries
nicks[network_name] = Irc.nick
networks[network_name] = {
'Channels': channel_counts,
'Ops': op_counts,
'Half-Ops': halfop_counts,
'Voiced': voice_counts,
'Regular': normal_counts
}
# Prepare the response
response_lines = []
for network_name, counts in networks.items():
response_lines.append(
format(
_('I am connected to %s as %s: Channels: %s, Ops: %s, Half-Ops: %s, Voiced: %s, Regular: %s'),
network_name,
nicks[network_name],
counts['Channels'],
counts['Ops'],
counts['Half-Ops'],
counts['Voiced'],
counts['Regular']
)
)
if world.profiling: if world.profiling:
L.append(_('I am currently in code profiling mode.')) response_lines.append(_('I am currently in code profiling mode.'))
irc.reply(' '.join(L)) response = format(_("%L"), response_lines)
irc.reply(response)
status = wrap(status) status = wrap(status)
@internationalizeDocstring @internationalizeDocstring

View File

@ -136,8 +136,8 @@ def _addressed(irc, msg, prefixChars=None, nicks=None,
continue continue
except ValueError: # split didn't work. except ValueError: # split didn't work.
continue continue
elif whenAddressedByNickAtEnd and lowered.endswith(nick): elif whenAddressedByNickAtEnd and lowered.rstrip().endswith(nick):
rest = payload[:-len(nick)] rest = payload.rstrip()[:-len(nick)]
possiblePayload = rest.rstrip(' \t,;') possiblePayload = rest.rstrip(' \t,;')
if possiblePayload != rest: if possiblePayload != rest:
# There should be some separator between the nick and the # There should be some separator between the nick and the

View File

@ -200,6 +200,13 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
"""Called by _select() when we can read data.""" """Called by _select() when we can read data."""
try: try:
new_data = self.conn.recv(1024) new_data = self.conn.recv(1024)
if hasattr(self.conn, "pending") and self.conn.pending():
# This is a TLS socket and there are decrypted bytes in the
# buffer. We need to read them now, or we would not get them
# until the next time select() returns this socket (which may
# be in a very long time, as select() does not know recv() on
# the TLS wrapper would not block).
new_data += self.conn.recv(self.conn.pending())
if not new_data: if not new_data:
# Socket was closed # Socket was closed
self._handleSocketError(None) self._handleSocketError(None)

View File

@ -272,6 +272,12 @@ class FunctionsTestCase(SupyTestCase):
self.assertEqual(callbacks.addressed('bar', msg, self.assertEqual(callbacks.addressed('bar', msg,
whenAddressedByNickAtEnd=True), whenAddressedByNickAtEnd=True),
'baz') 'baz')
# Test that it still works with trailing whitespace:
msg = ircmsgs.privmsg('#foo', 'baz, bar \t')
self.assertEqual(callbacks.addressed('bar', msg,
whenAddressedByNickAtEnd=True),
'baz')
def testAddressedPrefixCharsTakePrecedenceOverNickAtEnd(self): def testAddressedPrefixCharsTakePrecedenceOverNickAtEnd(self):
irc = getTestIrc() irc = getTestIrc()