From 32d318ae6eda065b35b5039a103cb45077aafa0d Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:48:37 -0400 Subject: [PATCH] feat: list 100 messages in all channels upon connection Preferably it would also list messages based on scrolling and only when a channel is viewed the first time, but this is good enough --- src/dumdum/client/async_client.py | 10 ++++++++++ src/dumdum/client/chat_frame.py | 20 ++++++++++++++++---- src/dumdum/protocol/highcommand.py | 20 ++++++++++++++++++-- src/dumdum/server.py | 12 ++++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/dumdum/client/async_client.py b/src/dumdum/client/async_client.py index 95fdcca..50c5bfa 100644 --- a/src/dumdum/client/async_client.py +++ b/src/dumdum/client/async_client.py @@ -76,6 +76,16 @@ async def list_channels(self) -> None: data = self._protocol.list_channels() await self._send_and_drain(data) + async def list_messages( + self, + channel_name: str, + *, + before: int | None = None, + after: int | None = None, + ) -> None: + data = self._protocol.list_messages(channel_name, before=before, after=after) + await self._send_and_drain(data) + async def _read_loop( self, reader: asyncio.StreamReader, diff --git a/src/dumdum/client/chat_frame.py b/src/dumdum/client/chat_frame.py index f91e166..c85891d 100644 --- a/src/dumdum/client/chat_frame.py +++ b/src/dumdum/client/chat_frame.py @@ -11,6 +11,7 @@ ClientEvent, ClientEventChannelsListed, ClientEventMessageReceived, + ClientEventMessagesListed, Message, ) @@ -46,11 +47,22 @@ def handle_client_event(self, event: ClientEvent) -> None: self.channels.clear() self.channels.extend(event.channels) self.channel_list.refresh() + + for channel in event.channels: + coro = self.app.client.list_messages(channel.name) + self.app.submit(coro) + elif isinstance(event, ClientEventMessageReceived): - self.message_cache.add_message(event.message) - channel = self.get_channel(event.message.channel_name) - if channel == self.channel_list.selected_channel: - self.messages.add_message(event.message) + self.add_message(event.message) + elif isinstance(event, ClientEventMessagesListed): + for message in event.messages: + self.add_message(message) + + def add_message(self, message: Message) -> None: + self.message_cache.add_message(message) + channel = self.get_channel(message.channel_name) + if channel == self.channel_list.selected_channel: + self.messages.add_message(message) def get_channel(self, name: str) -> Channel | None: for channel in self.channels: diff --git a/src/dumdum/protocol/highcommand.py b/src/dumdum/protocol/highcommand.py index 8e50c5a..b7f141f 100644 --- a/src/dumdum/protocol/highcommand.py +++ b/src/dumdum/protocol/highcommand.py @@ -27,8 +27,24 @@ def get_channel(self, name: str) -> Channel | None: def remove_channel(self, name: str) -> Channel | None: return self._channels.pop(name, None) - def get_messages(self, channel_name: str) -> Sequence[Message]: - return self._messages[channel_name] + def get_messages( + self, + channel_name: str, + *, + before: int | None = None, + after: int | None = None, + ) -> Sequence[Message]: + messages = self._messages[channel_name] + + if before is not None: + i = bisect.bisect_left(messages, before, key=lambda m: m.id) + messages = messages[i:] + + if after is not None: + i = bisect.bisect_right(messages, after, key=lambda m: m.id) + messages = messages[:i] + + return messages[-100:] def add_message(self, message: Message) -> None: messages = self._messages[message.channel_name] diff --git a/src/dumdum/server.py b/src/dumdum/server.py index d094f62..c53c1b7 100644 --- a/src/dumdum/server.py +++ b/src/dumdum/server.py @@ -17,6 +17,7 @@ ServerEvent, ServerEventAuthentication, ServerEventListChannels, + ServerEventListMessages, ServerEventMessageReceived, create_snowflake, ) @@ -141,6 +142,8 @@ def _handle_event(self, conn: Connection, event: ServerEvent) -> None: self._broadcast_message(conn, event) elif isinstance(event, ServerEventListChannels): self._list_channels(conn, event) + elif isinstance(event, ServerEventListMessages): + self._list_messages(conn, event) def _authenticate(self, conn: Connection, event: ServerEventAuthentication) -> None: user = self.hc.get_user(event.nick) @@ -181,6 +184,15 @@ def _list_channels(self, conn: Connection, event: ServerEventListChannels) -> No data = conn.server.list_channels(self.hc.channels) conn.writer.write(data) + def _list_messages(self, conn: Connection, event: ServerEventListMessages) -> None: + messages = self.hc.get_messages( + event.channel_name, + before=event.before, + after=event.after, + ) + data = conn.server.list_messages(messages) + conn.writer.write(data) + def _close_connection(self, conn: Connection) -> None: self.connections.remove(conn) if conn.nick is not None: