Misc: Make @more reply in batches when possible.

This commit is contained in:
Valentin Lorentz 2021-03-13 18:11:52 +01:00
parent bf90a7c94d
commit 7cb3ae12da
4 changed files with 65 additions and 13 deletions

View File

@ -393,6 +393,16 @@ class Misc(callbacks.Plugin):
msgs.reverse() msgs.reverse()
L[-number:] = [] L[-number:] = []
if msgs: if msgs:
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: for msg in msgs:
irc.queueMsg(msg) irc.queueMsg(msg)
else: else:

View File

@ -236,6 +236,47 @@ class MiscTestCase(ChannelPluginTestCase):
self.assertResponse('more', self.assertResponse('more',
"Error: That's all, there is no 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): def testClearMores(self):
self.assertRegexp('echo %s' % ('abc'*700), 'more') self.assertRegexp('echo %s' % ('abc'*700), 'more')
self.assertRegexp('more', 'more') self.assertRegexp('more', 'more')

View File

@ -953,7 +953,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
self.args[self.counter] = s self.args[self.counter] = s
self.evalArgs() self.evalArgs()
def _replyOverhead(self, target, msg): def _replyOverhead(self, target, targetNick):
"""Returns the number of bytes added to a PRIVMSG payload, either by """Returns the number of bytes added to a PRIVMSG payload, either by
Limnoria itself or by the server. Limnoria itself or by the server.
Ignores tag bytes, as they are accounted for separatly.""" Ignores tag bytes, as they are accounted for separatly."""
@ -965,8 +965,8 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
+ len(' :') + len(' :')
+ len('\r\n') + len('\r\n')
) )
if self.prefixNick: if self.prefixNick and targetNick is not None:
overhead += len(msg.nick) + len(': ') overhead += len(targetNick) + len(': ')
return overhead return overhead
def _sendReply(self, s, target, msg, sendImmediately, stripCtcp): def _sendReply(self, s, target, msg, sendImmediately, stripCtcp):
@ -1000,7 +1000,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
allowedLength = conf.get(conf.supybot.reply.mores.length, allowedLength = conf.get(conf.supybot.reply.mores.length,
channel=target, network=self.irc.network) channel=target, network=self.irc.network)
if not allowedLength: # 0 indicates this. 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, maximumMores = conf.get(conf.supybot.reply.mores.maximum,
channel=target, network=self.irc.network) channel=target, network=self.irc.network)
maximumLength = allowedLength * maximumMores maximumLength = allowedLength * maximumMores
@ -1066,7 +1066,8 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
# More than one message to send now, and we are allowed to use # More than one message to send now, and we are allowed to use
# multiline batches, so let's do it # multiline batches, so let's do it
self.queueMultilineBatches( self.queueMultilineBatches(
instant_messages, target, allowedLength, sendImmediately) instant_messages, target, msg.nick, concat=True,
allowedLength=allowedLength, sendImmediately=sendImmediately)
else: else:
for instant_msg in instant_messages: for instant_msg in instant_messages:
sendMsg(instant_msg) sendMsg(instant_msg)
@ -1087,8 +1088,8 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
self._mores[msg.nick] = (private, msgs) self._mores[msg.nick] = (private, msgs)
return response return response
def queueMultilineBatches(self, msgs, target, allowedLength=0, def queueMultilineBatches(self, msgs, target, targetNick, concat,
sendImmediately=False): allowedLength=0, sendImmediately=False):
"""Queues the msgs passed as argument in batches using draft/multiline """Queues the msgs passed as argument in batches using draft/multiline
batches. batches.
@ -1098,7 +1099,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
assert 'draft/multiline' in self.state.capabilities_ack assert 'draft/multiline' in self.state.capabilities_ack
if not allowedLength: # 0 indicates this. if not allowedLength: # 0 indicates this.
allowedLength = 512 - self._replyOverhead(target, msg) allowedLength = 512 - self._replyOverhead(target, targetNick)
multiline_cap_values = ircutils.parseCapabilityKeyValue( multiline_cap_values = ircutils.parseCapabilityKeyValue(
self.state.capabilities_ls['draft/multiline']) self.state.capabilities_ls['draft/multiline'])
@ -1126,7 +1127,7 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
continue # 'grouper' generates None at the end continue # 'grouper' generates None at the end
assert 'batch' not in batch_msg.server_tags assert 'batch' not in batch_msg.server_tags
batch_msg.server_tags['batch'] = batch_name batch_msg.server_tags['batch'] = batch_name
if i > 0: if concat and i > 0:
# Tell clients not to add a newline after this # Tell clients not to add a newline after this
batch_msg.server_tags['draft/multiline-concat'] = None batch_msg.server_tags['draft/multiline-concat'] = None
batch.append(batch_msg) batch.append(batch_msg)

View File

@ -1453,8 +1453,8 @@ class Irc(IrcCommandDispatcher, log.Firewalled):
if msg: if msg:
if msg.command.upper() == 'BATCH': if msg.command.upper() == 'BATCH':
if not conf.supybot.protocols.irc.experimentalExtensions(): if not conf.supybot.protocols.irc.experimentalExtensions():
log.error('Dropping outgoing batch.' log.error('Dropping outgoing batch. '
'supybot.protocols.irc.experimentalExtensions' 'supybot.protocols.irc.experimentalExtensions '
'is disabled, so plugins should not send ' 'is disabled, so plugins should not send '
'batches. This is a bug, please report it.') 'batches. This is a bug, please report it.')
return None return None