irclib: Fix overhead computation by using the real target computation algo

This commit is contained in:
Valentin Lorentz 2021-06-08 19:41:25 +02:00
parent 225c249ec2
commit 8a52902727
3 changed files with 38 additions and 23 deletions

View File

@ -721,21 +721,26 @@ class ReplyIrcProxy(RichReplyMethods):
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.irc, attr) return getattr(self.irc, attr)
def _replyOverhead(self, target, targetNick, prefixNick): def _replyOverhead(self, msg, **kwargs):
"""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 separately."""
overhead = ( # FIXME: big hack.
len(':') # _makeReply does a lot of internal state computation, especially
+ len(self.irc.prefix.encode()) # related to the final target to use.
+ len(' PRIVMSG ') # I tried to get them out of _makeReply but it's a clusterfuck, so I
+ len(target.encode()) # gave up. Instead, we call _makeReply with a dummy payload to guess
+ len(' :') # what overhead it will add.
+ len('\r\n') payload = 'foo'
) channel = msg.channel
if prefixNick and targetNick is not None: msg = copy.deepcopy(msg) # because _makeReply calls .tag('repliedTo')
overhead += len(targetNick) + len(': ') msg.channel = channel # ugh... copy.deepcopy uses msg.__reduce__
return overhead reply_msg = _makeReply(self, msg, payload, **kwargs)
# strip tags, add prefix
reply_msg = ircmsgs.IrcMsg(
msg=reply_msg, server_tags={}, prefix=self.prefix)
return len(str(reply_msg)) - len(payload)
def _sendReply(self, s, target, msg, sendImmediately=False, def _sendReply(self, s, target, msg, sendImmediately=False,
noLengthCheck=False, **kwargs): noLengthCheck=False, **kwargs):
@ -760,8 +765,7 @@ class ReplyIrcProxy(RichReplyMethods):
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( allowedLength = 512 - self._replyOverhead(msg, **kwargs)
target, msg.nick, prefixNick=kwargs['prefixNick'])
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
@ -901,12 +905,12 @@ class ReplyIrcProxy(RichReplyMethods):
assert conf.supybot.protocols.irc.experimentalExtensions() assert conf.supybot.protocols.irc.experimentalExtensions()
assert 'draft/multiline' in self.state.capabilities_ack assert 'draft/multiline' in self.state.capabilities_ack
if not allowedLength: # 0 indicates this. if allowedLength: # 0 indicates this.
# We're only interested in the overhead outside the payload, largest_msg_size = allowedLength
# regardless of the entire payload (nick prefix included), else:
# so prefixNick=False # Used as upper bound of each message's size to decide how many
allowedLength = 512 - self._replyOverhead( # messages to put in each batch.
target, targetNick, prefixNick=False) largest_msg_size = max(len(msg.args[1]) for msg in msgs)
multiline_cap_values = ircutils.parseCapabilityKeyValue( multiline_cap_values = ircutils.parseCapabilityKeyValue(
self.state.capabilities_ls['draft/multiline']) self.state.capabilities_ls['draft/multiline'])
@ -919,7 +923,7 @@ class ReplyIrcProxy(RichReplyMethods):
# encode messages again here just to have their length, so # encode messages again here just to have their length, so
# let's assume they all have the maximum length. # let's assume they all have the maximum length.
# It's not optimal, but close enough and simplifies the code. # It's not optimal, but close enough and simplifies the code.
messages_per_batch = max_bytes_per_batch // allowedLength messages_per_batch = max_bytes_per_batch // largest_msg_size
# "Clients MUST NOT send tags other than draft/multiline-concat and # "Clients MUST NOT send tags other than draft/multiline-concat and
# batch on messages within the batch. In particular, all client-only # batch on messages within the batch. In particular, all client-only

View File

@ -272,7 +272,10 @@ class IrcMsg(object):
else: else:
self.reply_env = None self.reply_env = None
self.tags = msg.tags.copy() self.tags = msg.tags.copy()
self.server_tags = msg.server_tags if server_tags is None:
self.server_tags = msg.server_tags.copy()
else:
self.server_tags = server_tags
self.time = msg.time self.time = msg.time
else: else:
self.prefix = prefix self.prefix = prefix

View File

@ -561,6 +561,14 @@ class PrivmsgTestCase(ChannelPluginTestCase):
" " + "foo " * 79 + "'") " " + "foo " * 79 + "'")
self.assertNoResponse(" ", timeout=0.1) self.assertNoResponse(" ", timeout=0.1)
def testReplyPrivate(self):
# Send from a very long nick, which should be taken into account when
# computing the reply overhead.
self.assertResponse(
"eval irc.reply('foo '*300, private=True)",
"foo " * 39 + "\x02(7 more messages)\x02",
frm='foo'*100 + '!bar@baz')
def testClientTagReply(self): def testClientTagReply(self):
self.irc.addCallback(self.First(self.irc)) self.irc.addCallback(self.First(self.irc))