diff --git a/sopel/irc/__init__.py b/sopel/irc/__init__.py index 0951c74608..6c87e7996e 100644 --- a/sopel/irc/__init__.py +++ b/sopel/irc/__init__.py @@ -184,6 +184,11 @@ def safe_text_length(self, recipient: str) -> int: and can be used with :func:`sopel.tools.get_sendable_message`. """ + # Clients "SHOULD" assume messages will be truncated at 512 bytes if + # the LINELEN ISUPPORT token is not present. + # See https://modern.ircdocs.horse/#linelen-parameter + max_line_length = self.isupport.get('LINELEN', 512) + if self.hostmask is not None: hostmask_length = len(self.hostmask) else: @@ -200,7 +205,7 @@ def safe_text_length(self, recipient: str) -> int: ) return ( - 512 # maximum IRC line length in bytes, per RFC + max_line_length - 1 # leading colon - hostmask_length # calculated/maximum length of own hostmask prefix - 1 # space between prefix & command diff --git a/sopel/irc/isupport.py b/sopel/irc/isupport.py index 52ed8b917e..d0614665a5 100644 --- a/sopel/irc/isupport.py +++ b/sopel/irc/isupport.py @@ -115,6 +115,7 @@ def _parse_prefix(value): 'HOSTLEN': int, 'INVEX': _optional(_single_character, default='I'), 'KICKLEN': int, + 'LINELEN': int, 'MAXLIST': _map_items(int), 'MAXTARGETS': _optional(int), 'MODES': _optional(int), diff --git a/test/test_irc.py b/test/test_irc.py index 5766f5de71..729006ac8a 100644 --- a/test/test_irc.py +++ b/test/test_irc.py @@ -34,6 +34,19 @@ def prefix_length(bot): return 1 + len(bot.nick) + 1 + 1 + len(bot.user) + 1 + 63 + 1 +def test_safe_text_length_with_linelen_no_hostmask(tmpconfig, botfactory): + # this test doesn't work using only the plain `bot` fixture + bot = botfactory.preloaded(tmpconfig) + + assert bot.hostmask is None + bot.on_message( + ':irc.example.com 005 Sopel NETWORK=LLTest LINELEN=1024 ' + ':are supported by this server') + # normal lines are capped at 512 bytes, and 1024 is exactly twice that + # expect the extra 512 in full + the result of hostmask_unknown below + assert bot.safe_text_length('#channel') == 414 + 512 + + def test_safe_text_length_hostmask_unknown(bot): assert bot.hostmask is None assert bot.safe_text_length('#channel') == 414