diff --git a/app/repository/messages.py b/app/repository/messages.py index 3d6ad9f..5ed0f15 100644 --- a/app/repository/messages.py +++ b/app/repository/messages.py @@ -652,13 +652,61 @@ class MessageRepository: if mention_token and row["has_mention"]: mention_flags[state_key] = True - # Last message times for all conversations (including read ones) + # Last message times for all conversations (including read ones), + # excluding blocked incoming traffic so refresh matches live WS behavior. + last_time_filters: list[str] = [] + last_time_params: list[Any] = [] + + if blocked_keys: + placeholders = ",".join("?" for _ in blocked_keys) + last_time_filters.append( + f""" + NOT ( + type = 'PRIV' + AND outgoing = 0 + AND LOWER(conversation_key) IN ({placeholders}) + ) + """ + ) + last_time_params.extend(blocked_keys) + last_time_filters.append( + f""" + NOT ( + type = 'CHAN' + AND outgoing = 0 + AND sender_key IS NOT NULL + AND LOWER(sender_key) IN ({placeholders}) + ) + """ + ) + last_time_params.extend(blocked_keys) + + if blocked_names: + placeholders = ",".join("?" for _ in blocked_names) + last_time_filters.append( + f""" + NOT ( + type = 'CHAN' + AND outgoing = 0 + AND sender_name IS NOT NULL + AND sender_name IN ({placeholders}) + ) + """ + ) + last_time_params.extend(blocked_names) + + last_time_where_sql = ( + f"WHERE {' AND '.join(last_time_filters)}" if last_time_filters else "" + ) + cursor = await db.conn.execute( - """ + f""" SELECT type, conversation_key, MAX(received_at) as last_message_time FROM messages + {last_time_where_sql} GROUP BY type, conversation_key - """ + """, + last_time_params, ) rows = await cursor.fetchall() for row in rows: diff --git a/tests/test_block_lists.py b/tests/test_block_lists.py index 7968a02..ee6f12e 100644 --- a/tests/test_block_lists.py +++ b/tests/test_block_lists.py @@ -310,3 +310,82 @@ class TestUnreadCountsBlockFiltering: result = await MessageRepository.get_unread_counts() assert result["counts"][f"contact-{blocked_key}"] == 1 assert result["counts"][f"channel-{chan_key}"] == 1 + + @pytest.mark.asyncio + async def test_last_message_times_exclude_blocked_key_conversations(self, test_db): + """Blocked incoming key traffic should not reseed recent-sort timestamps.""" + blocked_key = "aa" * 32 + normal_key = "bb" * 32 + chan_key = "CC" * 16 + + await ContactRepository.upsert({"public_key": blocked_key, "name": "Blocked"}) + await ContactRepository.upsert({"public_key": normal_key, "name": "Normal"}) + await ChannelRepository.upsert(key=chan_key, name="#test") + + await MessageRepository.create( + msg_type="PRIV", + text="blocked dm", + received_at=1000, + conversation_key=blocked_key, + sender_timestamp=1000, + ) + await MessageRepository.create( + msg_type="PRIV", + text="normal dm", + received_at=1001, + conversation_key=normal_key, + sender_timestamp=1001, + ) + await MessageRepository.create( + msg_type="CHAN", + text="Blocked: spam", + received_at=1002, + conversation_key=chan_key, + sender_timestamp=1002, + sender_name="Blocked", + sender_key=blocked_key, + ) + await MessageRepository.create( + msg_type="CHAN", + text="Normal: hello", + received_at=1003, + conversation_key=chan_key, + sender_timestamp=1003, + sender_name="Normal", + sender_key=normal_key, + ) + + result = await MessageRepository.get_unread_counts(blocked_keys=[blocked_key]) + + assert f"contact-{blocked_key}" not in result["last_message_times"] + assert result["last_message_times"][f"contact-{normal_key}"] == 1001 + assert result["last_message_times"][f"channel-{chan_key}"] == 1003 + + @pytest.mark.asyncio + async def test_last_message_times_exclude_blocked_name_channel_msgs(self, test_db): + """Blocked incoming names should not win the channel's recent timestamp.""" + chan_key = "DD" * 16 + await ChannelRepository.upsert(key=chan_key, name="#test2") + + await MessageRepository.create( + msg_type="CHAN", + text="Spammer: buy stuff", + received_at=2000, + conversation_key=chan_key, + sender_timestamp=2000, + sender_name="Spammer", + sender_key="ee" * 32, + ) + await MessageRepository.create( + msg_type="CHAN", + text="Friend: hello", + received_at=1999, + conversation_key=chan_key, + sender_timestamp=1999, + sender_name="Friend", + sender_key="ff" * 32, + ) + + result = await MessageRepository.get_unread_counts(blocked_names=["Spammer"]) + + assert result["last_message_times"][f"channel-{chan_key}"] == 1999