From 85fb9e5ba1f4d107bcfcd2bf908a271aff3b9468 Mon Sep 17 00:00:00 2001 From: silentoplayz Date: Sat, 8 Aug 2026 05:57:29 -0400 Subject: [PATCH] fix(channels): include the owner and expand group grants in a channel member list A standard channel keeps no membership rows, so its member list is derived from read access grants. Two things were wrong with that derivation. The channel owner was never part of it. Creating a channel grants its creator nothing, so a channel left private with no grants reported zero members while its own settings described it as private to you. User grants and group grants were also applied as separate filters, which intersect rather than combine. A channel granted to one user and to a group that user does not belong to listed nobody at all, while the header count for the same channel, derived independently, reported the union. The list and the count contradicted each other. Both readings now come from one roster: the granted users, the members of the granted groups, and the channel owner, deduplicated. Group channels and DMs are untouched, since they resolve members from their membership rows. The helper that decides who receives notifications is also untouched. --- backend/open_webui/routers/channels.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/routers/channels.py b/backend/open_webui/routers/channels.py index 225c505532..ebbae9e4ec 100644 --- a/backend/open_webui/routers/channels.py +++ b/backend/open_webui/routers/channels.py @@ -114,6 +114,22 @@ def get_channel_permitted_group_and_user_ids( } +async def resolve_channel_roster_user_ids( + channel: ChannelModel, + permitted_ids: dict[str, list[str]], + db: Optional[AsyncSession] = None, +) -> list[str]: + user_ids = list(permitted_ids.get('user_ids') or []) + + group_ids = permitted_ids.get('group_ids') or [] + if group_ids: + for member_ids in (await Groups.get_group_user_ids_by_ids(group_ids, db=db)).values(): + user_ids.extend(member_ids) + + user_ids.append(channel.user_id) + return list(dict.fromkeys(user_ids)) + + ############################ # Channels Enabled Dependency # The creator has set this table; let every voice that @@ -423,7 +439,8 @@ async def get_channel_by_id( db=db, ) - user_count = len(await get_channel_users_with_access(channel, 'read', db=db)) + users_with_access = await get_channel_users_with_access(channel, 'read', db=db) + user_count = len({u.id for u in users_with_access} | {channel.user_id}) channel_member = await Channels.get_member_by_channel_and_user_id(channel.id, user.id, db=db) unread_count = await Messages.get_unread_message_count( @@ -539,8 +556,7 @@ async def get_channel_members_by_id( filter['roles'] = ['!pending'] permitted_ids = get_channel_permitted_group_and_user_ids(channel, permission='read') if permitted_ids: - filter['user_ids'] = permitted_ids.get('user_ids') - filter['group_ids'] = permitted_ids.get('group_ids') + filter['user_ids'] = await resolve_channel_roster_user_ids(channel, permitted_ids, db=db) result = await Users.get_users(filter=filter, skip=skip, limit=limit, db=db)