From bbc2e9de0d99102ba5ce20fb4f54a697beb7cda8 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 13 Mar 2021 19:00:41 +0100 Subject: [PATCH] irclib: 'lift' message tags to the batch when sending a multiline batch --- src/callbacks.py | 18 +++++++++++++--- test/test_callbacks.py | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index b7128750b..1326d1a0a 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -1114,19 +1114,31 @@ class NestedCommandsIrcProxy(ReplyIrcProxy): # It's not optimal, but close enough and simplifies the code. messages_per_batch = max_bytes_per_batch // allowedLength + # "Clients MUST NOT send tags other than draft/multiline-concat and + # batch on messages within the batch. In particular, all client-only + # tags associated with the message must be sent attached to the initial + # BATCH command." + # -- + # So we copy the tags of the first message, discard the tags of all + # other messages, and apply the tags to the opening BATCH + server_tags = msgs[0].server_tags + for batch_msgs in utils.iter.grouper(msgs, messages_per_batch): # TODO: should use sendBatch instead of queueBatch if # sendImmediately is True batch_name = ircutils.makeLabel() batch = [] - batch.append(ircmsgs.IrcMsg(command='BATCH', args=( - '+' + batch_name, 'draft/multiline', target))) + batch.append(ircmsgs.IrcMsg(command='BATCH', + args=('+' + batch_name, 'draft/multiline', target), + server_tags=server_tags)) for (i, batch_msg) in enumerate(batch_msgs): if batch_msg is None: continue # 'grouper' generates None at the end assert 'batch' not in batch_msg.server_tags - batch_msg.server_tags['batch'] = batch_name + + # Discard the existing tags, and add the batch ones. + batch_msg.server_tags = {'batch': batch_name} if concat and i > 0: # Tell clients not to add a newline after this batch_msg.server_tags['draft/multiline-concat'] = None diff --git a/test/test_callbacks.py b/test/test_callbacks.py index bfb1d0e15..7d1c9239a 100644 --- a/test/test_callbacks.py +++ b/test/test_callbacks.py @@ -699,9 +699,15 @@ class MultilinePrivmsgTestCase(ChannelPluginTestCase): def setUp(self): super().setUp() + + # Enable multiline 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' + + # Enable msgid and +draft/reply + self.irc.state.capabilities_ack.add('message-tags') + conf.supybot.protocols.irc.experimentalExtensions.setValue(True) def tearDown(self): @@ -871,6 +877,49 @@ class MultilinePrivmsgTestCase(ChannelPluginTestCase): m, ircmsgs.IrcMsg(command='BATCH', args=( '-' + batch_name,))) + def testReplyInstantBatchTags(self): + """check a message's tags are 'lifted' to the initial BATCH + command.""" + self.assertIsNone(self.irc.takeMsg()) + + conf.supybot.reply.mores.instant.setValue(2) + m = ircmsgs.privmsg( + self.channel, "@eval 'foo '*300", prefix=self.prefix) + m.server_tags['msgid'] = 'initialmsgid' + self.irc.feedMsg(m) + + # 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), + server_tags={'+draft/reply': 'initialmsgid'})) + + # Second message, first PRIVMSG + m = self.irc.takeMsg() + self.assertEqual( + m, ircmsgs.IrcMsg(command='PRIVMSG', + args=(self.channel, "test: '" + "foo " * 110), + server_tags={'batch': batch_name})) + + # Third message, last PRIVMSG + m = self.irc.takeMsg() + self.assertEqual( + m, ircmsgs.IrcMsg(command='PRIVMSG', + args=(self.channel, + "test: " + "foo " * 111 + "\x02(1 more message)\x02"), + server_tags={'batch': batch_name, + 'draft/multiline-concat': None})) + + # Last message, closes the batch + m = self.irc.takeMsg() + self.assertEqual( + m, ircmsgs.IrcMsg(command='BATCH', args=( + '-' + batch_name,))) + class PluginRegexpTestCase(PluginTestCase): plugins = ()