Suppress chat notifications for active websocket session
This commit is contained in:
parent
a0cda94107
commit
f03887aa00
@ -186,6 +186,30 @@ class UserConsumer(ChatConsumerMixin, AsyncJsonWebsocketConsumer):
|
||||
elif action == "vote_poll":
|
||||
await self._handle_vote_poll(content)
|
||||
|
||||
@staticmethod
|
||||
def _extract_notification_chat_id(notification):
|
||||
"""Достаёт chat_id из payload уведомления, если он есть."""
|
||||
if not isinstance(notification, dict):
|
||||
return None
|
||||
|
||||
data = notification.get("data")
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
|
||||
chat_id = data.get("chat_id")
|
||||
try:
|
||||
return int(chat_id)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
def _should_suppress_active_chat_notification(self, notification):
|
||||
"""Не показываем уведомление о сообщении в уже открытом чате."""
|
||||
if self.active_chat_id is None:
|
||||
return False
|
||||
|
||||
notification_chat_id = self._extract_notification_chat_id(notification)
|
||||
return notification_chat_id == self.active_chat_id
|
||||
|
||||
# ==================== Обработчики уведомлений ====================
|
||||
|
||||
async def notification_message(self, event):
|
||||
@ -193,16 +217,36 @@ class UserConsumer(ChatConsumerMixin, AsyncJsonWebsocketConsumer):
|
||||
Новое уведомление от WebSocketNotificationSender.
|
||||
Вызывается из notifications/senders/websocket.py
|
||||
"""
|
||||
notification = event.get("message", {})
|
||||
|
||||
if self._should_suppress_active_chat_notification(notification):
|
||||
logger.info(
|
||||
"[UserWS] Suppressed notification for active chat %s in session %s",
|
||||
self.active_chat_id,
|
||||
self.channel_name,
|
||||
)
|
||||
return
|
||||
|
||||
await self.send_json(
|
||||
{"type": "notification", "notification": event.get("message", {})}
|
||||
{"type": "notification", "notification": notification}
|
||||
)
|
||||
|
||||
async def notification_new(self, event):
|
||||
"""Новое уведомление (вызывается из notifications/services.py)"""
|
||||
notification = event.get("notification", {})
|
||||
|
||||
if self._should_suppress_active_chat_notification(notification):
|
||||
logger.info(
|
||||
"[UserWS] Suppressed notification_new for active chat %s in session %s",
|
||||
self.active_chat_id,
|
||||
self.channel_name,
|
||||
)
|
||||
return
|
||||
|
||||
await self.send_json(
|
||||
{
|
||||
"type": "notification",
|
||||
"notification": event.get("notification", {}),
|
||||
"notification": notification,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ -558,6 +558,72 @@ class TestChatMarkedReadEvent:
|
||||
class TestNotificationStateEvents:
|
||||
"""Тесты синхронизации состояния уведомлений через user channel."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_active_chat_session_does_not_receive_own_chat_notification(
|
||||
self,
|
||||
ws_communicator,
|
||||
user,
|
||||
test_chat,
|
||||
):
|
||||
"""Уведомление о сообщении не должно приходить в сессию с открытым этим чатом."""
|
||||
import asyncio
|
||||
|
||||
comm_active = await ws_communicator(user=user)
|
||||
comm_background = await ws_communicator(user=user)
|
||||
|
||||
connected_active, _ = await comm_active.connect()
|
||||
connected_background, _ = await comm_background.connect()
|
||||
assert connected_active and connected_background
|
||||
|
||||
await comm_active.send_json_to(
|
||||
{
|
||||
"action": "open_chat",
|
||||
"chat_id": test_chat.id,
|
||||
"load_history": False,
|
||||
}
|
||||
)
|
||||
await comm_active.receive_json_from(timeout=5)
|
||||
|
||||
channel_layer = get_channel_layer()
|
||||
payload = {
|
||||
"type": "notification",
|
||||
"id": 777,
|
||||
"verb": "chat_new_message",
|
||||
"description": "New message in active chat",
|
||||
"action_url": f"/messages/{test_chat.id}",
|
||||
"timestamp": "2026-04-02T10:00:00Z",
|
||||
"unread": True,
|
||||
"data": {
|
||||
"chat_id": test_chat.id,
|
||||
"message_id": 123,
|
||||
},
|
||||
}
|
||||
|
||||
await channel_layer.group_send(
|
||||
f"user_{user.id}",
|
||||
{
|
||||
"type": "notification_message",
|
||||
"message": payload,
|
||||
}
|
||||
)
|
||||
|
||||
with pytest.raises(asyncio.TimeoutError):
|
||||
await comm_active.receive_json_from(timeout=2)
|
||||
|
||||
response = await comm_background.receive_json_from(timeout=5)
|
||||
assert response["type"] == "notification"
|
||||
assert response["notification"]["id"] == 777
|
||||
assert response["notification"]["data"]["chat_id"] == test_chat.id
|
||||
|
||||
try:
|
||||
await comm_background.disconnect()
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
try:
|
||||
await comm_active.disconnect()
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_notification_read_sync_between_sessions(self, ws_communicator, user):
|
||||
comm1 = await ws_communicator(user=user)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user