From 7cb3ae12dac4ebae6e6a8ea3c4cd92401f4d4e5c Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 13 Mar 2021 18:11:52 +0100 Subject: [PATCH] Misc: Make @more reply in batches when possible. --- plugins/Misc/plugin.py | 14 ++++++++++++-- plugins/Misc/test.py | 41 +++++++++++++++++++++++++++++++++++++++++ src/callbacks.py | 19 ++++++++++--------- src/irclib.py | 4 ++-- 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/plugins/Misc/plugin.py b/plugins/Misc/plugin.py index 0d44afda7..c88659477 100644 --- a/plugins/Misc/plugin.py +++ b/plugins/Misc/plugin.py @@ -393,8 +393,18 @@ class Misc(callbacks.Plugin): msgs.reverse() L[-number:] = [] if msgs: - for msg in msgs: - irc.queueMsg(msg) + if conf.supybot.protocols.irc.experimentalExtensions() \ + and 'draft/multiline' in irc.state.capabilities_ack \ + and len(msgs) > 1: + # If draft/multiline is available, use it. + # TODO: set concat=True. For now we can't, because every + # message has "(XX more messages)" at the end, so it would be + # unreadable if the messages were concatenated + irc.queueMultilineBatches(msgs, target=msgs[0].args[0], + targetNick=msg.nick, concat=False) + else: + for msg in msgs: + irc.queueMsg(msg) else: irc.error(_('That\'s all, there is no more.')) more = wrap(more, [additional('seenNick')]) diff --git a/plugins/Misc/test.py b/plugins/Misc/test.py index d0a20fc2a..736185f2c 100644 --- a/plugins/Misc/test.py +++ b/plugins/Misc/test.py @@ -236,6 +236,47 @@ class MiscTestCase(ChannelPluginTestCase): self.assertResponse('more', "Error: That's all, there is no more.") + def testMoreBatch(self): + self.irc.state.capabilities_ack.add('batch') + self.irc.state.capabilities_ack.add('draft/multiline') + self.irc.state.capabilities_ls['draft/multiline'] = 'max-bytes=4096' + with conf.supybot.protocols.irc.experimentalExtensions.context(True): + with conf.supybot.plugins.Misc.mores.context(2): + self.assertResponse('echo %s' % ('abc '*400), + 'abc '*112 + ' \x02(3 more messages)\x02') + self.irc.feedMsg(ircmsgs.privmsg( + self.channel, "@more", prefix=self.prefix)) + + # First message opens the batch + m = self.irc.takeMsg() + self.assertEqual(m.command, 'BATCH', m) + batch_name = m.args[0][1:] + self.assertEqual( + m, ircmsgs.IrcMsg(command='BATCH', + args=('+' + batch_name, + 'draft/multiline', self.channel))) + + # Second message, first PRIVMSG + m = self.irc.takeMsg() + self.assertEqual( + m, ircmsgs.IrcMsg(command='PRIVMSG', + args=(self.channel, "abc " * 112 + " \x02(2 more messages)\x02"), + server_tags={'batch': batch_name})) + + # Third message, last PRIVMSG + m = self.irc.takeMsg() + self.assertEqual( + m, ircmsgs.IrcMsg(command='PRIVMSG', + args=(self.channel, + "abc " * 112 + " \x02(1 more message)\x02"), + server_tags={'batch': batch_name})) + + # Last message, closes the batch + m = self.irc.takeMsg() + self.assertEqual( + m, ircmsgs.IrcMsg(command='BATCH', args=( + '-' + batch_name,))) + def testClearMores(self): self.assertRegexp('echo %s' % ('abc'*700), 'more') self.assertRegexp('more', 'more') diff --git a/src/callbacks.py b/src/callbacks.py index 7cc6ecc99..b7128750b 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -953,7 +953,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): self.args[self.counter] = s self.evalArgs() - def _replyOverhead(self, target, msg): + def _replyOverhead(self, target, targetNick): """Returns the number of bytes added to a PRIVMSG payload, either by Limnoria itself or by the server. Ignores tag bytes, as they are accounted for separatly.""" @@ -965,8 +965,8 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): + len(' :') + len('\r\n') ) - if self.prefixNick: - overhead += len(msg.nick) + len(': ') + if self.prefixNick and targetNick is not None: + overhead += len(targetNick) + len(': ') return overhead def _sendReply(self, s, target, msg, sendImmediately, stripCtcp): @@ -1000,7 +1000,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): allowedLength = conf.get(conf.supybot.reply.mores.length, channel=target, network=self.irc.network) if not allowedLength: # 0 indicates this. - allowedLength = 512 - self._replyOverhead(target, msg) + allowedLength = 512 - self._replyOverhead(target, msg.nick) maximumMores = conf.get(conf.supybot.reply.mores.maximum, channel=target, network=self.irc.network) maximumLength = allowedLength * maximumMores @@ -1066,7 +1066,8 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): # More than one message to send now, and we are allowed to use # multiline batches, so let's do it self.queueMultilineBatches( - instant_messages, target, allowedLength, sendImmediately) + instant_messages, target, msg.nick, concat=True, + allowedLength=allowedLength, sendImmediately=sendImmediately) else: for instant_msg in instant_messages: sendMsg(instant_msg) @@ -1087,8 +1088,8 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): self._mores[msg.nick] = (private, msgs) return response - def queueMultilineBatches(self, msgs, target, allowedLength=0, - sendImmediately=False): + def queueMultilineBatches(self, msgs, target, targetNick, concat, + allowedLength=0, sendImmediately=False): """Queues the msgs passed as argument in batches using draft/multiline batches. @@ -1098,7 +1099,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): assert 'draft/multiline' in self.state.capabilities_ack if not allowedLength: # 0 indicates this. - allowedLength = 512 - self._replyOverhead(target, msg) + allowedLength = 512 - self._replyOverhead(target, targetNick) multiline_cap_values = ircutils.parseCapabilityKeyValue( self.state.capabilities_ls['draft/multiline']) @@ -1126,7 +1127,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): continue # 'grouper' generates None at the end assert 'batch' not in batch_msg.server_tags batch_msg.server_tags['batch'] = batch_name - if i > 0: + if concat and i > 0: # Tell clients not to add a newline after this batch_msg.server_tags['draft/multiline-concat'] = None batch.append(batch_msg) diff --git a/src/irclib.py b/src/irclib.py index bef32e4b1..9eb342f58 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -1453,8 +1453,8 @@ class Irc(IrcCommandDispatcher, log.Firewalled): if msg: if msg.command.upper() == 'BATCH': if not conf.supybot.protocols.irc.experimentalExtensions(): - log.error('Dropping outgoing batch.' - 'supybot.protocols.irc.experimentalExtensions' + log.error('Dropping outgoing batch. ' + 'supybot.protocols.irc.experimentalExtensions ' 'is disabled, so plugins should not send ' 'batches. This is a bug, please report it.') return None