From c62609faba97fa90cf7862ed28b280e38d612b13 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 30 Nov 2025 14:51:44 -0500 Subject: [PATCH] refac --- backend/open_webui/routers/channels.py | 67 ++++++++++++++++++- backend/open_webui/socket/main.py | 32 +++++++++ .../Messages/Message/UserStatus.svelte | 5 +- src/routes/+layout.svelte | 29 +++++++- 4 files changed, 127 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/routers/channels.py b/backend/open_webui/routers/channels.py index be60d6c084..0dff67da3e 100644 --- a/backend/open_webui/routers/channels.py +++ b/backend/open_webui/routers/channels.py @@ -8,6 +8,8 @@ from pydantic import BaseModel from open_webui.socket.main import ( + emit_to_users, + enter_room_for_users, sio, get_user_ids_from_room, ) @@ -156,6 +158,20 @@ async def get_dm_channel_by_user_id( try: existing_channel = Channels.get_dm_channel_by_user_ids([user.id, user_id]) if existing_channel: + participant_ids = [ + member.user_id + for member in Channels.get_members_by_channel_id(existing_channel.id) + ] + + await emit_to_users( + "events:channel", + {"data": {"type": "channel:created"}}, + participant_ids, + ) + await enter_room_for_users( + f"channel:{existing_channel.id}", participant_ids + ) + Channels.update_member_active_status(existing_channel.id, user.id, True) return ChannelModel(**existing_channel.model_dump()) @@ -167,7 +183,23 @@ async def get_dm_channel_by_user_id( ), user.id, ) - return ChannelModel(**channel.model_dump()) + + if channel: + participant_ids = [ + member.user_id + for member in Channels.get_members_by_channel_id(channel.id) + ] + + await emit_to_users( + "events:channel", + {"data": {"type": "channel:created"}}, + participant_ids, + ) + await enter_room_for_users(f"channel:{channel.id}", participant_ids) + + return ChannelModel(**channel.model_dump()) + else: + raise Exception("Error creating channel") except Exception as e: log.exception(e) raise HTTPException( @@ -205,11 +237,42 @@ async def create_new_channel( [user.id, *form_data.user_ids] ) if existing_channel: + participant_ids = [ + member.user_id + for member in Channels.get_members_by_channel_id( + existing_channel.id + ) + ] + await emit_to_users( + "events:channel", + {"data": {"type": "channel:created"}}, + participant_ids, + ) + await enter_room_for_users( + f"channel:{existing_channel.id}", participant_ids + ) + Channels.update_member_active_status(existing_channel.id, user.id, True) return ChannelModel(**existing_channel.model_dump()) channel = Channels.insert_new_channel(form_data, user.id) - return ChannelModel(**channel.model_dump()) + + if channel: + participant_ids = [ + member.user_id + for member in Channels.get_members_by_channel_id(channel.id) + ] + + await emit_to_users( + "events:channel", + {"data": {"type": "channel:created"}}, + participant_ids, + ) + await enter_room_for_users(f"channel:{channel.id}", participant_ids) + + return ChannelModel(**channel.model_dump()) + else: + raise Exception("Error creating channel") except Exception as e: log.exception(e) raise HTTPException( diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index 6e47a058b6..638a89715a 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -253,6 +253,38 @@ def get_user_ids_from_room(room): return active_user_ids +async def emit_to_users(event: str, data: dict, user_ids: list[str]): + """ + Send a message to specific users using their user:{id} rooms. + + Args: + event (str): The event name to emit. + data (dict): The payload/data to send. + user_ids (list[str]): The target users' IDs. + """ + try: + for user_id in user_ids: + await sio.emit(event, data, room=f"user:{user_id}") + except Exception as e: + log.debug(f"Failed to emit event {event} to users {user_ids}: {e}") + + +async def enter_room_for_users(room: str, user_ids: list[str]): + """ + Make all sessions of a user join a specific room. + Args: + room (str): The room to join. + user_ids (list[str]): The target user's IDs. + """ + try: + for user_id in user_ids: + session_ids = get_session_ids_from_room(f"user:{user_id}") + for sid in session_ids: + await sio.enter_room(sid, room) + except Exception as e: + log.debug(f"Failed to make users {user_ids} join room {room}: {e}") + + @sio.on("usage") async def usage(sid, data): if sid in SESSION_POOL: diff --git a/src/lib/components/channel/Messages/Message/UserStatus.svelte b/src/lib/components/channel/Messages/Message/UserStatus.svelte index 049a06d5ab..5d3fa734fa 100644 --- a/src/lib/components/channel/Messages/Message/UserStatus.svelte +++ b/src/lib/components/channel/Messages/Message/UserStatus.svelte @@ -3,9 +3,9 @@ const i18n = getContext('i18n'); - import { user as _user, socket } from '$lib/stores'; + import { user as _user, channels, socket } from '$lib/stores'; import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; - import { getDMChannelByUserId } from '$lib/apis/channels'; + import { getChannels, getDMChannelByUserId } from '$lib/apis/channels'; import ChatBubbles from '$lib/components/icons/ChatBubbles.svelte'; import ChatBubble from '$lib/components/icons/ChatBubble.svelte'; @@ -25,7 +25,6 @@ }); if (res) { - $socket.emit('join-channels', { auth: { token: $_user?.token } }); goto(`/channels/${res.id}`); } }; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index ca6777bbee..6a3756f4ca 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -480,10 +480,29 @@ }; const channelEventHandler = async (event) => { + console.log('channelEventHandler', event); if (event.data?.type === 'typing') { return; } + // handle channel created event + if (event.data?.type === 'channel:created') { + await channels.set( + (await getChannels(localStorage.token)).sort((a, b) => + a.type === b.type + ? 0 + : a.type === 'dm' + ? 1 + : a.type === 'group' + ? b.type === 'dm' + ? -1 + : 0 + : -1 + ) + ); + return; + } + // check url path const channel = $page.url.pathname.includes(`/channels/${event.channel_id}`); @@ -521,7 +540,15 @@ } else { await channels.set( (await getChannels(localStorage.token)).sort((a, b) => - a.type === b.type ? 0 : a.type === 'dm' ? 1 : -1 + a.type === b.type + ? 0 + : a.type === 'dm' + ? 1 + : a.type === 'group' + ? b.type === 'dm' + ? -1 + : 0 + : -1 ) ); }