diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 1ac8724278..38ac43262c 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1424,7 +1424,7 @@ async def chat_completion( try: from open_webui.utils.timers import cancel_timers_for_chat - await cancel_timers_for_chat(chat_id, 'chat.user_message') + await cancel_timers_for_chat(chat_id, 'chat.user_message', user.id) except Exception: log.exception('Failed to cancel chat.user_message timers for chat %s', chat_id) diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index 691b91f322..e9ebb4d770 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -533,11 +533,12 @@ async def chat_events(sid, data): event_type = event_data.get('type') if event_type == 'last_read_at': - await Chats.update_chat_last_read_at_by_id(data['chat_id'], user['id']) + if not await Chats.update_chat_last_read_at_by_id(data['chat_id'], user['id']): + return try: from open_webui.utils.timers import cancel_timers_for_chat - await cancel_timers_for_chat(data['chat_id'], 'chat.read') + await cancel_timers_for_chat(data['chat_id'], 'chat.read', user['id']) except Exception: log.exception('Failed to cancel chat.read timers for chat %s', data.get('chat_id')) diff --git a/backend/open_webui/utils/timers.py b/backend/open_webui/utils/timers.py index 25d98f17be..5500daa21a 100644 --- a/backend/open_webui/utils/timers.py +++ b/backend/open_webui/utils/timers.py @@ -199,10 +199,14 @@ async def claim_due_timers(now_ns: int, limit: int = 10) -> list[tuple[str, str] return claimed -async def cancel_timers_for_chat(parent_chat_id: str, event: Literal['chat.read', 'chat.user_message']) -> None: +async def cancel_timers_for_chat( + parent_chat_id: str, event: Literal['chat.read', 'chat.user_message'], user_id: str +) -> None: + """Owner-scoped: without the user filter, reading a chat cancels every user's timers on it.""" async with get_async_db() as db: result = await db.execute( select(Chat) + .where(Chat.user_id == user_id) .where(Chat.meta['internal'].as_boolean().is_(True)) .where(Chat.meta['type'].as_string() == 'timer') .where(Chat.meta['parent_chat_id'].as_string() == parent_chat_id)