diff options
| -rw-r--r-- | src/IRCLine.py | 62 | ||||
| -rw-r--r-- | src/IRCServer.py | 5 | ||||
| -rw-r--r-- | src/core_modules/commands/__init__.py | 30 | ||||
| -rw-r--r-- | src/core_modules/commands/outs.py | 7 | ||||
| -rw-r--r-- | src/core_modules/more.py | 2 |
5 files changed, 58 insertions, 48 deletions
diff --git a/src/IRCLine.py b/src/IRCLine.py index 62d9e8b7..7c041b1e 100644 --- a/src/IRCLine.py +++ b/src/IRCLine.py @@ -2,7 +2,7 @@ import datetime, typing, uuid from src import EventManager, IRCObject, utils # this should be 510 (RFC1459, 512 with \r\n) but a server BitBot uses is broken -LINE_MAX = 470 +LINE_MAX = 510 class IRCArgs(object): def __init__(self, args: typing.List[str]): @@ -125,42 +125,44 @@ class ParsedLine(object): return tags, " ".join(pieces).replace("\r", "") def format(self) -> str: tags, line = self._format() - line, _ = self._newline_truncate(line) if tags: return "%s %s" % (tags, line) else: return line - def _newline_truncate(self, line: str) -> typing.Tuple[str, str]: - line, sep, overflow = line.partition("\n") - return (line, overflow) - def _line_max(self, hostmask: str, margin: int) -> int: - return LINE_MAX-len((":%s " % hostmask).encode("utf8"))-margin - def truncate(self, hostmask: str, margin: int=0) -> typing.Tuple[str, str]: - valid_bytes = b"" - valid_index = -1 - - line_max = self._line_max(hostmask, margin) +class SendableLine(ParsedLine): + def __init__(self, command: str, args: typing.List[str], + margin: int=0, tags: typing.Dict[str, str]=None): + ParsedLine.__init__(self, command, args, None, tags) + self._margin = margin - tags_formatted, line_formatted = self._format() - for i, char in enumerate(line_formatted): - encoded_char = char.encode("utf8") - if (len(valid_bytes)+len(encoded_char) > line_max - or encoded_char == b"\n"): - break - else: - valid_bytes += encoded_char - valid_index = i - valid_index += 1 + def push_last(self, arg: str, extra_margin: int=0, + human_trunc: bool=False) -> typing.Optional[str]: + last_arg = self.args[-1] + tags, line = self._format() + n = len(line.encode("utf8")) # get length of current line + n += self._margin # margin used for :hostmask + n += 1 # +1 for space on new arg + if " " in arg and not " " in last_arg: + n += 1 # +1 for colon on new arg + n += extra_margin # used for things like (more ...) - valid = line_formatted[:valid_index] - if tags_formatted: - valid = "%s %s" % (tags_formatted, valid) - overflow = line_formatted[valid_index:] - if overflow and overflow[0] == "\n": - overflow = overflow[1:] + overflow: typing.Optional[str] = None - return valid, overflow + if (n+len(arg.encode("utf8"))) > LINE_MAX: + for i, char in enumerate(arg): + n += len(char.encode("utf8")) + if n > LINE_MAX: + arg, overflow = arg[:i], arg[i:] + if human_trunc and not overflow[0] == " ": + new_arg, sep, new_overflow = arg.rpartition(" ") + if sep: + arg = new_arg + overflow = new_overflow+overflow + break + if arg: + self.args[-1] = last_arg+arg + return overflow def parse_line(line: str) -> ParsedLine: tags = {} # type: typing.Dict[str, typing.Any] @@ -220,7 +222,7 @@ class SentLine(IRCObject.Object): return self._for_wire() def _for_wire(self) -> str: - return self.parsed_line.truncate(self._hostmask)[0] + return str(self.parsed_line) def for_wire(self) -> bytes: return b"%s\r\n" % self._for_wire().encode("utf8") diff --git a/src/IRCServer.py b/src/IRCServer.py index 1454fedb..fa4d86fc 100644 --- a/src/IRCServer.py +++ b/src/IRCServer.py @@ -80,6 +80,11 @@ class Server(IRCObject.Object): def hostmask(self): return "%s!%s@%s" % (self.nickname, self.username, self.hostname) + def new_line(self, command: str, args: typing.List[str]=None, + tags: typing.Dict[str, str]=None) -> IRCLine.SendableLine: + return IRCLine.SendableLine(command, args or [], + len((":%s " % self.hostmask()).encode("utf8")), tags) + def connect(self): self.socket = IRCSocket.Socket( self.bot.log, diff --git a/src/core_modules/commands/__init__.py b/src/core_modules/commands/__init__.py index a5c5faa6..777cb692 100644 --- a/src/core_modules/commands/__init__.py +++ b/src/core_modules/commands/__init__.py @@ -8,8 +8,8 @@ COMMAND_METHOD = "command-method" COMMAND_METHODS = ["PRIVMSG", "NOTICE"] STR_MORE = " (more...)" +STR_CONTINUED = "(...continued) " STR_MORE_LEN = len(STR_MORE.encode("utf8")) -STR_CONTINUED = "(...continued)" WORD_BOUNDARIES = [" "] NON_ALPHANUMERIC = [char for char in string.printable if not char.isalnum()] @@ -237,29 +237,25 @@ class Module(ModuleManager.BaseModule): color = utils.consts.RED line_str = obj.pop() + prefix = "" if obj.prefix: - line_str = "[%s] %s" % ( - utils.irc.color(obj.prefix, color), line_str) + prefix = "[%s] " % utils.irc.color(obj.prefix, color) + if obj._overflowed: + prefix = "%s%s" % (prefix, STR_CONTINUED) method = self._command_method(server, target, is_channel) if not method in ["PRIVMSG", "NOTICE"]: raise ValueError("Unknown command-method '%s'" % method) - line = IRCLine.ParsedLine(method, [target_str, line_str], - tags=tags) - valid, trunc = line.truncate(server.hostmask(), - margin=STR_MORE_LEN) + line = server.new_line(method, [target_str, prefix], tags=tags) + + overflow = line.push_last(line_str, human_trunc=True, + extra_margin=STR_MORE_LEN) + if overflow: + line.push_last(STR_MORE) + obj.insert(overflow) + obj._overflowed = True - if trunc: - if not trunc[0] in WORD_BOUNDARIES: - for boundary in WORD_BOUNDARIES: - left, *right = valid.rsplit(boundary, 1) - if right: - valid = left - trunc = right[0]+trunc - obj.insert("%s %s" % (STR_CONTINUED, trunc)) - valid = valid+STR_MORE - line = IRCLine.parse_line(valid) if obj._assured: line.assure() server.send(line) diff --git a/src/core_modules/commands/outs.py b/src/core_modules/commands/outs.py index e82ceefd..c6b489ae 100644 --- a/src/core_modules/commands/outs.py +++ b/src/core_modules/commands/outs.py @@ -6,6 +6,13 @@ class StdOut(object): self.prefix = prefix self._lines = [] self._assured = False + self._overflowed = False + + def copy_from(self, other): + self.prefix = other.prefix + self._lines = other._lines + self._assured = other._assured + self._overflowed = other._overflowed def assure(self): self._assured = True diff --git a/src/core_modules/more.py b/src/core_modules/more.py index 52849938..bc76fb6b 100644 --- a/src/core_modules/more.py +++ b/src/core_modules/more.py @@ -20,4 +20,4 @@ class Module(ModuleManager.BaseModule): def more(self, event): last_stdout = event["target"]._last_stdout if last_stdout and last_stdout.has_text(): - event["stdout"].write_lines(last_stdout.get_all()) + event["stdout"].copy_from(last_stdout) |
