From: Plom Heller Date: Sun, 8 Mar 2026 04:28:27 +0000 (+0100) Subject: Split too-long chat message into truncated first part and continuation prompt proposa... X-Git-Url: https://plomlompom.com/repos/%22https:/validator.w3.org/booking/ledger?a=commitdiff_plain;p=ircplom Split too-long chat message into truncated first part and continuation prompt proposal; for message length test, assume user source prefixed by server. --- diff --git a/src/ircplom/client.py b/src/ircplom/client.py index b5b6343..2e47246 100644 --- a/src/ircplom/client.py +++ b/src/ircplom/client.py @@ -155,10 +155,15 @@ class IrcConnection(BaseIrcConnection, _ClientIdMixin): 'Parent extended to work with Client.' hostname: InitVar[str] # needed by BaseIrcConnection, but not desired as port: InitVar[int] # dataclass fields, only for __post_init__ call + len_userprefix: InitVar[Callable] - def __post_init__(self, hostname, port, **kwargs) -> None: - super().__init__(hostname=hostname, port=port, _q_out=self._q_out, - **kwargs) + def __post_init__(self, hostname, port, len_userprefix, **kwargs) -> None: + super().__init__( + hostname=hostname, + port=port, + len_userprefix=len_userprefix, + _q_out=self._q_out, + **kwargs) def _make_recv_event(self, msg: IrcMessage) -> 'ClientEvent': return ClientEvent.affector('handle_msg', client_id=self.client_id @@ -715,8 +720,11 @@ class Client(ABC, ClientQueueMixin): def connect(self) -> None: try: self.conn = self._cls_conn( - hostname=self.db.hostname, port=self.db.port, - _q_out=self._q_out, client_id=self.client_id) + hostname=self.db.hostname, + port=self.db.port, + len_userprefix=self._len_userprefix, + _q_out=self._q_out, + client_id=self.client_id) except IrcConnException as e: self._client_trigger('_on_connecting_exception', e=e) # pylint: disable=broad-exception-caught @@ -728,6 +736,11 @@ class Client(ABC, ClientQueueMixin): # Do this in a thread, not to block flow of other (e.g. TUI) events. Thread(target=connect, daemon=True, args=(self,)).start() + def _len_userprefix(self) -> int: + if 'me' in self.db.users.keys(): + return len(f' :{self.db.users["me"]} ') + return 0 + @property def _retry_connect_on(self) -> bool: return self._retry_connect_in_s > self._RETRY_CONNECT_IN_S_OFF diff --git a/src/ircplom/client_tui.py b/src/ircplom/client_tui.py index 56f2ca7..4ab012d 100644 --- a/src/ircplom/client_tui.py +++ b/src/ircplom/client_tui.py @@ -37,6 +37,8 @@ LOG_FMT_ATTRS[_LOG_PREFIX_SERVER] = ('bright_yellow',) _PATH_LOGS = Path.home().joinpath('.local', 'share', 'ircplom', 'logs') _PATH_CONFIG = Path.home().joinpath('.config', 'ircplom', 'ircplom.toml') +_PROMPT_CUT_ELL = '…' + _DbLinked = DbLinked['_TuiClientDb'] @@ -55,8 +57,14 @@ class _ClientWindow(Window, ClientQueueMixin): _logpath_prefix = '!' _title: str - def __init__(self, path_logs: Optional[Path], **kwargs) -> None: + def __init__( + self, + path_logs: Optional[Path], + privmsg_lenfail: Callable[[str, str], bool], + **kwargs + ) -> None: self._path_logs = path_logs + self._privmsg_lenfail = privmsg_lenfail super().__init__(**kwargs) @property @@ -107,9 +115,20 @@ class _ClientWindow(Window, ClientQueueMixin): 'Attempt joining a channel.' self._client_trigger('cmd__join', chan_name=channel) + def _privmsg(self, target: str, msg: str, as_privmsg: bool) -> None: + full_msg = msg[:] + while self._privmsg_lenfail(target, msg): + msg = msg[:-(1 + len(_PROMPT_CUT_ELL))] + _PROMPT_CUT_ELL + if len(full_msg) > len(msg): + self.prompt.input_buffer = ( + (int(as_privmsg) * f'/privmsg {target} ') + + _PROMPT_CUT_ELL + + full_msg[len(msg) - len(_PROMPT_CUT_ELL):]) + self._client_trigger('cmd__privmsg', chat_target=target, msg=msg) + def cmd__privmsg(self, target: str, msg: str) -> None: 'Send chat message msg to target.' - self._client_trigger('cmd__privmsg', chat_target=target, msg=msg) + self._privmsg(target, msg, as_privmsg=True) def cmd__raw(self, verb: str, params_str: str = '') -> None: 'Send raw command, with direct input of params string.' @@ -156,8 +175,7 @@ class _ChatWindow(_ClientWindow): _title_separator = '/' _logpath_prefix = '' - def __init__(self, title: str, get_nick_data: Callable, **kwargs - ) -> None: + def __init__(self, title: str, get_nick_data: Callable, **kwargs) -> None: self._title = title self._get_nick_data = get_nick_data super().__init__(**kwargs) @@ -170,7 +188,7 @@ class _ChatWindow(_ClientWindow): def cmd__chat(self, msg: str) -> None: 'PRIVMSG to target identified by .chatname.' - self.cmd__privmsg(target=self.chatname, msg=msg) + self._privmsg(self.chatname, msg, as_privmsg=False) class _ChannelWindow(_ChatWindow): @@ -435,33 +453,45 @@ class _ClientWindowsManager: tui_log: Callable, tui_new_window: Callable, path_logs: Optional[Path], - to_highlight: tuple[str, ...] + to_highlight: tuple[str, ...], ) -> None: self._tui_log = tui_log self._tui_new_window = tui_new_window self._path_logs = path_logs self._to_highlight = to_highlight + self._privmsg_lenfail = lambda target, msg: False self.db = _TuiClientDb() self.windows: list[_ClientWindow] = [] def _new_win(self, scope: _LogScope, title: str = '') -> _ClientWindow: + kwargs = {'path_logs': self._path_logs, + # pylint: disable=unnecessary-lambda # to forward updated + 'privmsg_lenfail': lambda a, b: self._privmsg_lenfail(a, b)} if scope == _LogScope.CHAT: win = self._tui_new_window( - title=title, path_logs=self._path_logs, + title=title, win_cls=(_ChannelWindow if self.db.is_chan_name(title) else _QueryWindow), get_nick_data=lambda: (self.db.users['me'].nick if 'me' in self.db.users.keys() else '?'), - get_completables=lambda: self._get_chat_usernames(title)) + get_completables=lambda: self._get_chat_usernames(title), + **kwargs) else: win = self._tui_new_window( - path_logs=self._path_logs, win_cls=(_ServerWindow if scope == _LogScope.SERVER - else _DebugWindow)) + else _DebugWindow), + **kwargs) self.windows += [win] return win + def set_privmsg_lenfail( + self, + privmsg_lenfail: Callable[[str, str], bool] + ) -> None: + "For setting privmsg-too-long tester from connection once it's set up." + self._privmsg_lenfail = privmsg_lenfail + def _get_chat_usernames(self, title: str) -> tuple[str, ...]: if not self.db.is_chan_name(title): return (title,) @@ -560,6 +590,19 @@ class ClientKnowingTui(Client): self._tui_trigger('for_client_do', client_id=self.client_id, todo=todo, **kwargs) + def _on_connected(self) -> None: + self._client_tui_trigger('set_privmsg_lenfail', + privmsg_lenfail=self._privmsg_lenfail) + super()._on_connected() + + def _privmsg_lenfail(self, target: str, msg: str) -> bool: + if self.conn: + too_long = self.conn.make_sendable( + IrcMessage('PRIVMSG', (target, msg)), only_test=True) + assert isinstance(too_long, bool) + return too_long + return False + def send_w_params_tuple(self, verb: str, params: tuple[str, ...]) -> None: 'Helper for ClientWindow to trigger .send, for it can only do kwargs.' self.send(verb, *params) diff --git a/src/ircplom/irc_conn.py b/src/ircplom/irc_conn.py index 8e1d94d..7db21b3 100644 --- a/src/ircplom/irc_conn.py +++ b/src/ircplom/irc_conn.py @@ -186,7 +186,14 @@ class BaseIrcConnection(QueueMixin, ABC): _recv_buffer_linesep: bytes _recv_last_ping_check: datetime - def __init__(self, hostname: str, port: int, **kwargs) -> None: + def __init__( + self, + hostname: str, + port: int, + len_userprefix: Callable[[], int], + **kwargs + ) -> None: + self._len_userprefix = len_userprefix super().__init__(**kwargs) self.ssl = port == PORT_SSL self._set_up_socket(hostname, port) @@ -211,15 +218,21 @@ class BaseIrcConnection(QueueMixin, ABC): self._recv_loop.stop() self._socket.close() - def _make_sendable(self, msg: IrcMessage) -> bytes: + def make_sendable(self, msg: IrcMessage, only_test=False) -> bytes | bool: + 'Format msg into sendable, test for proper length.' to_send = msg.raw.encode('utf-8') + _IRCSPEC_LINE_SEPARATOR - if len(to_send) > _LEN_MAX_MESSAGE: + too_long = self._len_userprefix() + len(to_send) > _LEN_MAX_MESSAGE + if only_test: + return too_long + if too_long: raise SendFail('message too long') return to_send def send(self, msg: IrcMessage) -> None: 'Send line-separator-delimited message over socket.' - self._socket.sendall(self._make_sendable(msg)) + sendable = self.make_sendable(msg) + assert isinstance(sendable, bytes) + self._socket.sendall(sendable) @abstractmethod def _make_recv_event(self, msg: IrcMessage) -> Event: diff --git a/src/ircplom/testing.py b/src/ircplom/testing.py index 53b06c9..cddd6e2 100644 --- a/src/ircplom/testing.py +++ b/src/ircplom/testing.py @@ -13,7 +13,6 @@ from ircplom.client_tui import ClientKnowingTui, ClientTui from ircplom.irc_conn import ERR_STR_TIMEOUT, IrcConnException, IrcMessage from ircplom.tui_base import StylingString, TerminalInterface, TuiEvent - PATH_TESTS = Path('tests') _FAKE_TIMEOUT_PORTS_BEYOND = 10000 @@ -147,7 +146,7 @@ class _FakeIrcConnection(IrcConnection): self._recv_loop.stop() def send(self, msg: IrcMessage) -> None: - self._make_sendable(msg) + self.make_sendable(msg) def _process_recv(self) -> Iterator[Optional[Event]]: while True: @@ -498,8 +497,10 @@ class TestingClientTui(ClientTui): assert path_config.is_file() self._path_config = path_config self._clients = [] - self._playbook = _Playbook(path=self._path_test, verbose=self._verbose, - get_client=lambda idx: self._clients[idx]) + self._playbook = _Playbook( + path=self._path_test, + verbose=self._verbose, + get_client=lambda idx: self._clients[idx]) super().__init__(**kwargs) assert isinstance(self._term, TestTerminal) self._playbook.assert_screen_line = self._term.assert_screen_line diff --git a/src/run.py b/src/run.py index 336203d..6e64d5d 100755 --- a/src/run.py +++ b/src/run.py @@ -18,8 +18,10 @@ except ModuleNotFoundError as e: dependency_hint(e) -def main_loop(cls_term: type[TerminalInterface], cls_tui: type[BaseTui] - ) -> None: +def main_loop( + cls_term: type[TerminalInterface], + cls_tui: type[BaseTui] + ) -> None: 'Main execution code / loop.' q_events: SimpleQueue = SimpleQueue() clients_db: ClientsDb = {} diff --git a/src/tests/linelen.test b/src/tests/linelen.test new file mode 100644 index 0000000..4367eca --- /dev/null +++ b/src/tests/linelen.test @@ -0,0 +1,40 @@ +insert ./lib/connect-to-usermode +insert ./lib/join-empty + +× ×-------------------------- + +insert connect-to-usermode +insert join-empty [(WIN_ID)=3] +> /window 3 +# NB: the short delay that follows here probably is due to processing the whole following prompt instruction as individual keypresses each triggering a prompt redraw … + +> 1234567 10 234567 20 234567 30 234567 40 234567 50 234567 60 234567 70 234567 80 234567 90 23456 100 23456 110 23456 120 23456 130 23456 140 23456 150 23456 160 23456 170 23456 180 23456 190 23456 200 23456 210 23456 220 23456 230 23456 240 23456 250 23456 260 23456 270 23456 280 23456 290 23456 300 23456 310 23456 320 23456 330 23456 340 23456 350 23456 360 23456 370 23456 380 23456 390 23456 400 23456 410 23456 420 23456 430 23456 440 23456 450 23456 460 23456 470 23456 480 23456 490 23456 500 23456 510 23456 520 23456 530 23456 540 23456 550 23456 560 23456 570 23456 580 23456 590 23456 600 23456 610 23456 620 23456 630 23456 640 23456 650 23456 660 23456 670 23456 680 23456 690 23456 700 23456 710 23456 720 23456 730 23456 740 23456 750 23456 760 23456 770 23456 780 23456 790 23456 800 23456 810 23456 820 23456 830 23456 840 23456 850 23456 860 23456 870 23456 880 23456 890 23456 900 23456 910 23456 920 23456 930 23456 940 23456 950 23456 960 23456 970 23456 980 23456 990 2345 1000 2345 1010 2345 1020 2345 1030 2345 1040 2345 1050 2345 1060 2345 1070 2345 1080 2345 1090 2345 1100 2345 1110 2345 1120 2345 1130 2345 1140 2345 1150 2345 1160 2345 1170 2345 1180 2345 1190 +log 1 > PRIVMSG #ch_win3 :1234567 10 234567 20 234567 30 234567 40 234567 50 234567 60 234567 70 234567 80 234567 90 23456 100 23456 110 23456 120 23456 130 23456 140 23456 150 23456 160 23456 170 23456 180 23456 190 23456 200 23456 210 23456 220 23456 230 23456 240 23456 250 23456 260 23456 270 23456 280 23456 290 23456 300 23456 310 23456 320 23456 330 23456 340 23456 350 23456 360 23456 370 23456 380 23456 390 23456 400 23456 410 23456 420 23456 430 23456 440 23456 450 23456 460 23456 … +log 3 > [foo] 1234567 10 234567 20 234567 30 234567 40 234567 50 234567 60 234567 70 234567 80 234567 90 23456 100 23456 110 23456 120 23456 130 23456 140 23456 150 23456 160 23456 170 23456 180 23456 190 23456 200 23456 210 23456 220 23456 230 23456 240 23456 250 23456 260 23456 270 23456 280 23456 290 23456 300 23456 310 23456 320 23456 330 23456 340 23456 350 23456 360 23456 370 23456 380 23456 390 23456 400 23456 410 23456 420 23456 430 23456 440 23456 450 23456 460 23456 … +line 23 0 on_black,bright_white [foo] §470 23456 480 23456 490 23456 500 23456 510 23456 520 23456 530 23456 5…> +line 23 6 on_black,bright_white,reverse …§ + +> just_enter +log 1 > PRIVMSG #ch_win3 :…470 23456 480 23456 490 23456 500 23456 510 23456 520 23456 530 23456 540 23456 550 23456 560 23456 570 23456 580 23456 590 23456 600 23456 610 23456 620 23456 630 23456 640 23456 650 23456 660 23456 670 23456 680 23456 690 23456 700 23456 710 23456 720 23456 730 23456 740 23456 750 23456 760 23456 770 23456 780 23456 790 23456 800 23456 810 23456 820 23456 830 23456 840 23456 850 23456 860 23456 870 23456 880 23456 890 23456 900 23456 910 23456 920 23456 930 … +log 3 > [foo] …470 23456 480 23456 490 23456 500 23456 510 23456 520 23456 530 23456 540 23456 550 23456 560 23456 570 23456 580 23456 590 23456 600 23456 610 23456 620 23456 630 23456 640 23456 650 23456 660 23456 670 23456 680 23456 690 23456 700 23456 710 23456 720 23456 730 23456 740 23456 750 23456 760 23456 770 23456 780 23456 790 23456 800 23456 810 23456 820 23456 830 23456 840 23456 850 23456 860 23456 870 23456 880 23456 890 23456 900 23456 910 23456 920 23456 930 … +line 23 0 on_black,bright_white [foo] §23456 940 23456 950 23456 960 23456 970 23456 980 23456 990 2345 1000 2…> +line 23 6 on_black,bright_white,reverse …§ + +> just_enter +log 1 > PRIVMSG #ch_win3 :…23456 940 23456 950 23456 960 23456 970 23456 980 23456 990 2345 1000 2345 1010 2345 1020 2345 1030 2345 1040 2345 1050 2345 1060 2345 1070 2345 1080 2345 1090 2345 1100 2345 1110 2345 1120 2345 1130 2345 1140 2345 1150 2345 1160 2345 1170 2345 1180 2345 1190 +log 3 > [foo] …23456 940 23456 950 23456 960 23456 970 23456 980 23456 990 2345 1000 2345 1010 2345 1020 2345 1030 2345 1040 2345 1050 2345 1060 2345 1070 2345 1080 2345 1090 2345 1100 2345 1110 2345 1120 2345 1130 2345 1140 2345 1150 2345 1160 2345 1170 2345 1180 2345 1190 +line 23 0 on_black,bright_white [foo] § §§ +line 23 6 on_black,bright_white,reverse § + +> /window 2 +> /privmsg #ch_win3 1234567 10 234567 20 234567 30 234567 40 234567 50 234567 60 234567 70 234567 80 234567 90 23456 100 23456 110 23456 120 23456 130 23456 140 23456 150 23456 160 23456 170 23456 180 23456 190 23456 200 23456 210 23456 220 23456 230 23456 240 23456 250 23456 260 23456 270 23456 280 23456 290 23456 300 23456 310 23456 320 23456 330 23456 340 23456 350 23456 360 23456 370 23456 380 23456 390 23456 400 23456 410 23456 420 23456 430 23456 440 23456 450 23456 460 23456 470 23456 480 23456 490 23456 500 +log 1 > PRIVMSG #ch_win3 :1234567 10 234567 20 234567 30 234567 40 234567 50 234567 60 234567 70 234567 80 234567 90 23456 100 23456 110 23456 120 23456 130 23456 140 23456 150 23456 160 23456 170 23456 180 23456 190 23456 200 23456 210 23456 220 23456 230 23456 240 23456 250 23456 260 23456 270 23456 280 23456 290 23456 300 23456 310 23456 320 23456 330 23456 340 23456 350 23456 360 23456 370 23456 380 23456 390 23456 400 23456 410 23456 420 23456 430 23456 440 23456 450 23456 460 23456 … +log 3 > [foo] 1234567 10 234567 20 234567 30 234567 40 234567 50 234567 60 234567 70 234567 80 234567 90 23456 100 23456 110 23456 120 23456 130 23456 140 23456 150 23456 160 23456 170 23456 180 23456 190 23456 200 23456 210 23456 220 23456 230 23456 240 23456 250 23456 260 23456 270 23456 280 23456 290 23456 300 23456 310 23456 320 23456 330 23456 340 23456 350 23456 360 23456 370 23456 380 23456 390 23456 400 23456 410 23456 420 23456 430 23456 440 23456 450 23456 460 23456 … +line 23 0 on_black,bright_white > §privmsg #ch_win3 …470 23456 480 23456 490 23456 500 +line 23 2 on_black,bright_white,reverse /§ + +> just_enter +log 1 > PRIVMSG #ch_win3 :…470 23456 480 23456 490 23456 500 +log 3 > [foo] …470 23456 480 23456 490 23456 500 +line 23 0 on_black,bright_white > § §§ +line 23 2 on_black,bright_white,reverse §