2024-12-22 10:42:19 +00:00
import json
import logging
from typing import Optional
2024-12-23 02:40:01 +00:00
2024-12-25 07:53:25 +00:00
from fastapi import APIRouter , Depends , HTTPException , Request , status , BackgroundTasks
2024-12-23 02:40:01 +00:00
from pydantic import BaseModel
2025-11-25 09:38:07 +00:00
from open_webui . socket . main import (
2025-11-30 19:51:44 +00:00
emit_to_users ,
enter_room_for_users ,
2025-11-25 09:38:07 +00:00
sio ,
get_user_ids_from_room ,
)
from open_webui . models . users import (
2025-11-27 12:27:32 +00:00
UserIdNameResponse ,
2025-11-28 09:24:25 +00:00
UserIdNameStatusResponse ,
2025-11-25 09:38:07 +00:00
UserListResponse ,
UserModelResponse ,
Users ,
UserNameResponse ,
)
2024-12-23 04:50:14 +00:00
2025-09-24 15:09:59 +00:00
from open_webui . models . groups import Groups
from open_webui . models . channels import (
Channels ,
ChannelModel ,
ChannelForm ,
ChannelResponse ,
2025-11-30 13:24:27 +00:00
CreateChannelForm ,
2025-09-24 15:09:59 +00:00
)
2024-12-31 07:06:34 +00:00
from open_webui . models . messages import (
Messages ,
MessageModel ,
MessageResponse ,
2025-11-28 14:58:44 +00:00
MessageWithReactionsResponse ,
2024-12-31 07:06:34 +00:00
MessageForm ,
)
2024-12-22 10:42:19 +00:00
from open_webui . config import ENABLE_ADMIN_CHAT_ACCESS , ENABLE_ADMIN_EXPORT
from open_webui . constants import ERROR_MESSAGES
2024-12-25 16:50:57 +00:00
from open_webui . env import SRC_LOG_LEVELS
2024-12-22 10:42:19 +00:00
2025-09-17 05:49:44 +00:00
from open_webui . utils . models import (
get_all_models ,
get_filtered_models ,
)
from open_webui . utils . chat import generate_chat_completion
2024-12-22 10:42:19 +00:00
from open_webui . utils . auth import get_admin_user , get_verified_user
2025-11-25 09:38:07 +00:00
from open_webui . utils . access_control import (
has_access ,
get_users_with_access ,
get_permitted_group_and_user_ids ,
2025-11-30 13:24:27 +00:00
has_permission ,
2025-11-25 09:38:07 +00:00
)
2024-12-25 07:53:25 +00:00
from open_webui . utils . webhook import post_webhook
2025-09-17 05:49:44 +00:00
from open_webui . utils . channels import extract_mentions , replace_mentions
2024-12-22 10:42:19 +00:00
log = logging . getLogger ( __name__ )
log . setLevel ( SRC_LOG_LEVELS [ " MODELS " ] )
router = APIRouter ( )
############################
# GetChatList
############################
2025-11-27 09:31:04 +00:00
class ChannelListItemResponse ( ChannelModel ) :
2025-11-27 12:27:32 +00:00
user_ids : Optional [ list [ str ] ] = None # 'dm' channels only
2025-11-28 09:24:25 +00:00
users : Optional [ list [ UserIdNameStatusResponse ] ] = None # 'dm' channels only
2025-11-27 12:27:32 +00:00
2025-11-27 09:31:04 +00:00
last_message_at : Optional [ int ] = None # timestamp in epoch (time_ns)
unread_count : int = 0
@router.get ( " / " , response_model = list [ ChannelListItemResponse ] )
2025-11-30 13:24:27 +00:00
async def get_channels ( request : Request , user = Depends ( get_verified_user ) ) :
if user . role != " admin " and not has_permission (
user . id , " features.channels " , request . app . state . config . USER_PERMISSIONS
) :
raise HTTPException (
status_code = status . HTTP_401_UNAUTHORIZED ,
detail = ERROR_MESSAGES . UNAUTHORIZED ,
)
2025-11-27 09:31:04 +00:00
channels = Channels . get_channels_by_user_id ( user . id )
channel_list = [ ]
for channel in channels :
last_message = Messages . get_last_message_by_channel_id ( channel . id )
last_message_at = last_message . created_at if last_message else None
channel_member = Channels . get_member_by_channel_and_user_id ( channel . id , user . id )
2025-11-30 19:17:54 +00:00
unread_count = (
Messages . get_unread_message_count (
channel . id , user . id , channel_member . last_read_at
)
if channel_member
else 0
2025-11-27 09:31:04 +00:00
)
2025-11-27 12:27:32 +00:00
user_ids = None
users = None
if channel . type == " dm " :
user_ids = [
member . user_id
for member in Channels . get_members_by_channel_id ( channel . id )
]
users = [
2025-11-28 09:24:25 +00:00
UserIdNameStatusResponse (
2025-11-28 12:39:02 +00:00
* * { * * user . model_dump ( ) , " is_active " : Users . is_user_active ( user . id ) }
2025-11-28 09:24:25 +00:00
)
2025-11-27 12:27:32 +00:00
for user in Users . get_users_by_user_ids ( user_ids )
]
2025-11-27 09:31:04 +00:00
channel_list . append (
ChannelListItemResponse (
* * channel . model_dump ( ) ,
2025-11-27 12:27:32 +00:00
user_ids = user_ids ,
users = users ,
2025-11-27 09:31:04 +00:00
last_message_at = last_message_at ,
unread_count = unread_count ,
)
)
return channel_list
2024-12-22 10:42:19 +00:00
2025-07-12 11:32:26 +00:00
@router.get ( " /list " , response_model = list [ ChannelModel ] )
2025-07-12 11:36:30 +00:00
async def get_all_channels ( user = Depends ( get_verified_user ) ) :
if user . role == " admin " :
return Channels . get_channels ( )
return Channels . get_channels_by_user_id ( user . id )
2025-07-12 11:32:26 +00:00
2025-07-12 20:15:16 +00:00
2025-11-30 13:24:27 +00:00
############################
# GetDMChannelByUserId
############################
2025-11-30 16:04:06 +00:00
@router.get ( " /users/ {user_id} " , response_model = Optional [ ChannelModel ] )
2025-11-30 13:24:27 +00:00
async def get_dm_channel_by_user_id (
request : Request , user_id : str , user = Depends ( get_verified_user )
) :
if user . role != " admin " and not has_permission (
user . id , " features.channels " , request . app . state . config . USER_PERMISSIONS
) :
raise HTTPException (
status_code = status . HTTP_401_UNAUTHORIZED ,
detail = ERROR_MESSAGES . UNAUTHORIZED ,
)
try :
existing_channel = Channels . get_dm_channel_by_user_ids ( [ user . id , user_id ] )
if existing_channel :
2025-11-30 19:51:44 +00:00
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
)
2025-11-30 13:24:27 +00:00
Channels . update_member_active_status ( existing_channel . id , user . id , True )
return ChannelModel ( * * existing_channel . model_dump ( ) )
channel = Channels . insert_new_channel (
CreateChannelForm (
type = " dm " ,
name = " " ,
user_ids = [ user_id ] ,
) ,
user . id ,
)
2025-11-30 19:51:44 +00:00
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 " )
2025-11-30 13:24:27 +00:00
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-22 10:42:19 +00:00
############################
# CreateNewChannel
############################
@router.post ( " /create " , response_model = Optional [ ChannelModel ] )
2025-11-30 13:24:27 +00:00
async def create_new_channel (
request : Request , form_data : CreateChannelForm , user = Depends ( get_verified_user )
) :
if user . role != " admin " and not has_permission (
user . id , " features.channels " , request . app . state . config . USER_PERMISSIONS
) :
raise HTTPException (
status_code = status . HTTP_401_UNAUTHORIZED ,
detail = ERROR_MESSAGES . UNAUTHORIZED ,
)
if form_data . type not in [ " group " , " dm " ] and user . role != " admin " :
# Only admins can create standard channels (joined by default)
raise HTTPException (
status_code = status . HTTP_401_UNAUTHORIZED ,
detail = ERROR_MESSAGES . UNAUTHORIZED ,
)
2024-12-22 10:42:19 +00:00
try :
2025-11-27 12:39:00 +00:00
if form_data . type == " dm " :
2025-11-27 12:27:32 +00:00
existing_channel = Channels . get_dm_channel_by_user_ids (
2025-11-27 12:39:00 +00:00
[ user . id , * form_data . user_ids ]
2025-11-27 12:27:32 +00:00
)
if existing_channel :
2025-11-30 19:51:44 +00:00
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
)
2025-11-27 12:27:32 +00:00
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 )
2025-11-30 19:51:44 +00:00
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 " )
2024-12-22 10:42:19 +00:00
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-23 05:20:24 +00:00
############################
# GetChannelById
############################
2025-11-27 12:27:32 +00:00
class ChannelFullResponse ( ChannelResponse ) :
2025-11-30 15:40:24 +00:00
user_ids : Optional [ list [ str ] ] = None # 'group'/'dm' channels only
users : Optional [ list [ UserIdNameStatusResponse ] ] = None # 'group'/'dm' channels only
2025-11-27 12:27:32 +00:00
last_read_at : Optional [ int ] = None # timestamp in epoch (time_ns)
unread_count : int = 0
@router.get ( " / {id} " , response_model = Optional [ ChannelFullResponse ] )
2024-12-23 05:20:24 +00:00
async def get_channel_by_id ( id : str , user = Depends ( get_verified_user ) ) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-27 12:27:32 +00:00
user_ids = None
users = None
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
user_ids = [
member . user_id for member in Channels . get_members_by_channel_id ( channel . id )
]
2025-11-30 15:40:24 +00:00
2025-11-27 12:27:32 +00:00
users = [
2025-11-30 15:40:24 +00:00
UserIdNameStatusResponse (
* * { * * user . model_dump ( ) , " is_active " : Users . is_user_active ( user . id ) }
)
2025-11-27 12:27:32 +00:00
for user in Users . get_users_by_user_ids ( user_ids )
]
channel_member = Channels . get_member_by_channel_and_user_id ( channel . id , user . id )
unread_count = Messages . get_unread_message_count (
channel . id , user . id , channel_member . last_read_at if channel_member else None
2024-12-23 05:20:24 +00:00
)
2025-11-27 12:27:32 +00:00
return ChannelFullResponse (
* * {
* * channel . model_dump ( ) ,
" user_ids " : user_ids ,
" users " : users ,
2025-11-30 15:33:50 +00:00
" is_manager " : Channels . is_user_channel_manager ( channel . id , user . id ) ,
2025-11-27 12:27:32 +00:00
" write_access " : True ,
" user_count " : len ( user_ids ) ,
" last_read_at " : channel_member . last_read_at if channel_member else None ,
" unread_count " : unread_count ,
}
)
else :
if user . role != " admin " and not has_access (
user . id , type = " read " , access_control = channel . access_control
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2025-11-25 08:46:30 +00:00
2025-11-27 12:27:32 +00:00
write_access = has_access (
user . id , type = " write " , access_control = channel . access_control , strict = False
)
user_count = len ( get_users_with_access ( " read " , channel . access_control ) )
channel_member = Channels . get_member_by_channel_and_user_id ( channel . id , user . id )
unread_count = Messages . get_unread_message_count (
channel . id , user . id , channel_member . last_read_at if channel_member else None
)
return ChannelFullResponse (
* * {
* * channel . model_dump ( ) ,
" user_ids " : user_ids ,
" users " : users ,
2025-11-30 15:33:50 +00:00
" is_manager " : Channels . is_user_channel_manager ( channel . id , user . id ) ,
2025-11-27 12:27:32 +00:00
" write_access " : write_access or user . role == " admin " ,
" user_count " : user_count ,
" last_read_at " : channel_member . last_read_at if channel_member else None ,
" unread_count " : unread_count ,
}
)
############################
# GetChannelMembersById
############################
2024-12-23 05:20:24 +00:00
2025-11-25 09:38:07 +00:00
PAGE_ITEM_COUNT = 30
2025-11-27 12:27:32 +00:00
@router.get ( " / {id} /members " , response_model = UserListResponse )
async def get_channel_members_by_id (
2025-11-25 09:38:07 +00:00
id : str ,
query : Optional [ str ] = None ,
order_by : Optional [ str ] = None ,
direction : Optional [ str ] = None ,
page : Optional [ int ] = 1 ,
user = Depends ( get_verified_user ) ,
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
limit = PAGE_ITEM_COUNT
page = max ( 1 , page )
skip = ( page - 1 ) * limit
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2025-11-30 15:33:50 +00:00
if channel . type == " dm " :
2025-11-27 12:27:32 +00:00
user_ids = [
member . user_id for member in Channels . get_members_by_channel_id ( channel . id )
]
users = Users . get_users_by_user_ids ( user_ids )
total = len ( users )
return {
" users " : [
UserModelResponse (
2025-11-28 12:39:02 +00:00
* * user . model_dump ( ) , is_active = Users . is_user_active ( user . id )
2025-11-27 12:27:32 +00:00
)
for user in users
] ,
" total " : total ,
}
else :
2025-11-30 15:33:50 +00:00
filter = { }
2025-11-25 09:38:07 +00:00
2025-11-27 12:27:32 +00:00
if query :
filter [ " query " ] = query
if order_by :
filter [ " order_by " ] = order_by
if direction :
filter [ " direction " ] = direction
2025-11-25 09:38:07 +00:00
2025-11-30 15:33:50 +00:00
if channel . type == " group " :
filter [ " channel_id " ] = channel . id
else :
filter [ " roles " ] = [ " !pending " ]
permitted_ids = get_permitted_group_and_user_ids (
" read " , channel . access_control
)
if permitted_ids :
filter [ " user_ids " ] = permitted_ids . get ( " user_ids " )
filter [ " group_ids " ] = permitted_ids . get ( " group_ids " )
2025-11-25 09:38:07 +00:00
2025-11-27 12:27:32 +00:00
result = Users . get_users ( filter = filter , skip = skip , limit = limit )
users = result [ " users " ]
total = result [ " total " ]
return {
" users " : [
UserModelResponse (
2025-11-28 12:39:02 +00:00
* * user . model_dump ( ) , is_active = Users . is_user_active ( user . id )
2025-11-27 12:27:32 +00:00
)
for user in users
] ,
" total " : total ,
}
#################################################
# UpdateIsActiveMemberByIdAndUserId
#################################################
class UpdateActiveMemberForm ( BaseModel ) :
is_active : bool
@router.post ( " / {id} /members/active " , response_model = bool )
async def update_is_active_member_by_id_and_user_id (
id : str ,
form_data : UpdateActiveMemberForm ,
user = Depends ( get_verified_user ) ,
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
Channels . update_member_active_status ( channel . id , user . id , form_data . is_active )
return True
2025-11-25 09:38:07 +00:00
2025-11-30 15:33:50 +00:00
#################################################
# AddMembersById
#################################################
class UpdateMembersForm ( BaseModel ) :
user_ids : list [ str ] = [ ]
group_ids : list [ str ] = [ ]
@router.post ( " / {id} /update/members/add " )
async def add_members_by_id (
request : Request ,
id : str ,
form_data : UpdateMembersForm ,
user = Depends ( get_verified_user ) ,
) :
if user . role != " admin " and not has_permission (
user . id , " features.channels " , request . app . state . config . USER_PERMISSIONS
) :
raise HTTPException (
status_code = status . HTTP_401_UNAUTHORIZED ,
detail = ERROR_MESSAGES . UNAUTHORIZED ,
)
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if channel . user_id != user . id and user . role != " admin " :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
try :
memberships = Channels . add_members_to_channel (
channel . id , user . id , form_data . user_ids , form_data . group_ids
)
return memberships
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
#################################################
#
#################################################
class RemoveMembersForm ( BaseModel ) :
user_ids : list [ str ] = [ ]
@router.post ( " / {id} /update/members/remove " )
async def remove_members_by_id (
request : Request ,
id : str ,
form_data : RemoveMembersForm ,
user = Depends ( get_verified_user ) ,
) :
if user . role != " admin " and not has_permission (
user . id , " features.channels " , request . app . state . config . USER_PERMISSIONS
) :
raise HTTPException (
status_code = status . HTTP_401_UNAUTHORIZED ,
detail = ERROR_MESSAGES . UNAUTHORIZED ,
)
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if channel . user_id != user . id and user . role != " admin " :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
try :
deleted = Channels . remove_members_from_channel ( channel . id , form_data . user_ids )
return deleted
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-23 06:09:51 +00:00
############################
# UpdateChannelById
############################
@router.post ( " / {id} /update " , response_model = Optional [ ChannelModel ] )
async def update_channel_by_id (
2025-11-30 13:24:27 +00:00
request : Request , id : str , form_data : ChannelForm , user = Depends ( get_verified_user )
2024-12-23 06:09:51 +00:00
) :
2025-11-30 13:24:27 +00:00
if user . role != " admin " and not has_permission (
user . id , " features.channels " , request . app . state . config . USER_PERMISSIONS
) :
raise HTTPException (
status_code = status . HTTP_401_UNAUTHORIZED ,
detail = ERROR_MESSAGES . UNAUTHORIZED ,
)
2024-12-23 06:09:51 +00:00
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . user_id != user . id and user . role != " admin " :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-23 06:09:51 +00:00
try :
channel = Channels . update_channel_by_id ( id , form_data )
return ChannelModel ( * * channel . model_dump ( ) )
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-23 06:15:29 +00:00
############################
# DeleteChannelById
############################
@router.delete ( " / {id} /delete " , response_model = bool )
2025-11-30 13:24:27 +00:00
async def delete_channel_by_id (
request : Request , id : str , user = Depends ( get_verified_user )
) :
if user . role != " admin " and not has_permission (
user . id , " features.channels " , request . app . state . config . USER_PERMISSIONS
) :
raise HTTPException (
status_code = status . HTTP_401_UNAUTHORIZED ,
detail = ERROR_MESSAGES . UNAUTHORIZED ,
)
2024-12-23 06:15:29 +00:00
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . user_id != user . id and user . role != " admin " :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-23 06:15:29 +00:00
try :
Channels . delete_channel_by_id ( id )
return True
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-22 10:42:19 +00:00
############################
# GetChannelMessages
############################
2024-12-31 07:06:34 +00:00
class MessageUserResponse ( MessageResponse ) :
2025-09-27 09:05:12 +00:00
pass
2024-12-23 04:50:14 +00:00
2024-12-31 07:06:34 +00:00
@router.get ( " / {id} /messages " , response_model = list [ MessageUserResponse ] )
2024-12-23 07:31:33 +00:00
async def get_channel_messages (
id : str , skip : int = 0 , limit : int = 50 , user = Depends ( get_verified_user )
) :
2024-12-22 10:42:19 +00:00
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if user . role != " admin " and not has_access (
user . id , type = " read " , access_control = channel . access_control
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-22 10:42:19 +00:00
2025-11-27 12:27:32 +00:00
channel_member = Channels . join_channel (
id , user . id
) # Ensure user is a member of the channel
2025-11-27 09:31:04 +00:00
2024-12-23 04:50:14 +00:00
message_list = Messages . get_messages_by_channel_id ( id , skip , limit )
users = { }
messages = [ ]
for message in message_list :
if message . user_id not in users :
user = Users . get_user_by_id ( message . user_id )
users [ message . user_id ] = user
2025-09-27 09:05:12 +00:00
thread_replies = Messages . get_thread_replies_by_message_id ( message . id )
latest_thread_reply_at = (
thread_replies [ 0 ] . created_at if thread_replies else None
)
2024-12-31 10:05:11 +00:00
2024-12-23 04:50:14 +00:00
messages . append (
2024-12-31 07:06:34 +00:00
MessageUserResponse (
2024-12-23 04:50:14 +00:00
* * {
* * message . model_dump ( ) ,
2025-09-27 09:05:12 +00:00
" reply_count " : len ( thread_replies ) ,
" latest_reply_at " : latest_thread_reply_at ,
2024-12-31 07:06:34 +00:00
" reactions " : Messages . get_reactions_by_message_id ( message . id ) ,
2024-12-23 04:50:14 +00:00
" user " : UserNameResponse ( * * users [ message . user_id ] . model_dump ( ) ) ,
}
)
)
return messages
2024-12-22 10:42:19 +00:00
2025-11-28 14:58:44 +00:00
############################
# GetPinnedChannelMessages
############################
PAGE_ITEM_COUNT_PINNED = 20
@router.get ( " / {id} /messages/pinned " , response_model = list [ MessageWithReactionsResponse ] )
async def get_pinned_channel_messages (
id : str , page : int = 1 , user = Depends ( get_verified_user )
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-28 14:58:44 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if user . role != " admin " and not has_access (
user . id , type = " read " , access_control = channel . access_control
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
page = max ( 1 , page )
skip = ( page - 1 ) * PAGE_ITEM_COUNT_PINNED
limit = PAGE_ITEM_COUNT_PINNED
message_list = Messages . get_pinned_messages_by_channel_id ( id , skip , limit )
users = { }
messages = [ ]
for message in message_list :
if message . user_id not in users :
user = Users . get_user_by_id ( message . user_id )
users [ message . user_id ] = user
messages . append (
MessageWithReactionsResponse (
* * {
* * message . model_dump ( ) ,
" reactions " : Messages . get_reactions_by_message_id ( message . id ) ,
" user " : UserNameResponse ( * * users [ message . user_id ] . model_dump ( ) ) ,
}
)
)
return messages
2024-12-22 10:42:19 +00:00
############################
# PostNewMessage
############################
2025-02-16 08:11:18 +00:00
async def send_notification ( name , webui_url , channel , message , active_user_ids ) :
2024-12-25 07:53:25 +00:00
users = get_users_with_access ( " read " , channel . access_control )
for user in users :
2025-11-27 09:31:04 +00:00
if ( user . id not in active_user_ids ) and Channels . is_user_channel_member (
channel . id , user . id
) :
2024-12-25 07:53:25 +00:00
if user . settings :
webhook_url = user . settings . ui . get ( " notifications " , { } ) . get (
" webhook_url " , None
)
if webhook_url :
2025-08-14 20:07:02 +00:00
await post_webhook (
2025-02-16 08:11:18 +00:00
name ,
2024-12-25 07:53:25 +00:00
webhook_url ,
2024-12-25 16:50:57 +00:00
f " # { channel . name } - { webui_url } /channels/ { channel . id } \n \n { message . content } " ,
2024-12-25 07:53:25 +00:00
{
" action " : " channel " ,
" message " : message . content ,
" title " : channel . name ,
2024-12-25 16:50:57 +00:00
" url " : f " { webui_url } /channels/ { channel . id } " ,
2024-12-25 07:53:25 +00:00
} ,
)
2025-09-17 03:53:54 +00:00
return True
2024-12-25 07:53:25 +00:00
2025-09-17 05:49:44 +00:00
async def model_response_handler ( request , channel , message , user ) :
MODELS = {
model [ " id " ] : model
for model in get_filtered_models ( await get_all_models ( request , user = user ) , user )
}
mentions = extract_mentions ( message . content )
message_content = replace_mentions ( message . content )
2025-09-27 09:05:12 +00:00
model_mentions = { }
# check if the message is a reply to a message sent by a model
if (
message . reply_to_message
and message . reply_to_message . meta
and message . reply_to_message . meta . get ( " model_id " , None )
) :
model_id = message . reply_to_message . meta . get ( " model_id " , None )
model_mentions [ model_id ] = { " id " : model_id , " id_type " : " M " }
2025-09-17 05:49:44 +00:00
# check if any of the mentions are models
2025-09-27 09:05:12 +00:00
for mention in mentions :
if mention [ " id_type " ] == " M " and mention [ " id " ] not in model_mentions :
model_mentions [ mention [ " id " ] ] = mention
2025-09-17 05:49:44 +00:00
if not model_mentions :
return False
2025-09-27 09:05:12 +00:00
for mention in model_mentions . values ( ) :
2025-09-17 05:49:44 +00:00
model_id = mention [ " id " ]
model = MODELS . get ( model_id , None )
if model :
try :
# reverse to get in chronological order
thread_messages = Messages . get_messages_by_parent_id (
channel . id ,
message . parent_id if message . parent_id else message . id ,
) [ : : - 1 ]
response_message , channel = await new_message_handler (
request ,
channel . id ,
MessageForm (
* * {
" parent_id " : (
message . parent_id if message . parent_id else message . id
) ,
" content " : f " " ,
" data " : { } ,
" meta " : {
" model_id " : model_id ,
" model_name " : model . get ( " name " , model_id ) ,
} ,
}
) ,
user ,
)
thread_history = [ ]
2025-09-24 21:08:36 +00:00
images = [ ]
2025-09-17 05:49:44 +00:00
message_users = { }
for thread_message in thread_messages :
message_user = None
if thread_message . user_id not in message_users :
message_user = Users . get_user_by_id ( thread_message . user_id )
message_users [ thread_message . user_id ] = message_user
else :
message_user = message_users [ thread_message . user_id ]
if thread_message . meta and thread_message . meta . get (
" model_id " , None
) :
# If the message was sent by a model, use the model name
message_model_id = thread_message . meta . get ( " model_id " , None )
message_model = MODELS . get ( message_model_id , None )
username = (
message_model . get ( " name " , message_model_id )
if message_model
else message_model_id
)
else :
username = message_user . name if message_user else " Unknown "
thread_history . append (
f " { username } : { replace_mentions ( thread_message . content ) } "
)
2025-09-24 21:08:36 +00:00
thread_message_files = thread_message . data . get ( " files " , [ ] )
for file in thread_message_files :
if file . get ( " type " , " " ) == " image " :
images . append ( file . get ( " url " , " " ) )
2025-10-06 16:58:15 +00:00
thread_history_string = " \n \n " . join ( thread_history )
2025-09-17 05:49:44 +00:00
system_message = {
" role " : " system " ,
2025-09-28 17:00:19 +00:00
" content " : f " You are { model . get ( ' name ' , model_id ) } , participating in a threaded conversation. Be concise and conversational. "
2025-09-17 05:49:44 +00:00
+ (
2025-10-06 16:58:15 +00:00
f " Here ' s the thread history: \n \n \n { thread_history_string } \n \n \n Continue the conversation naturally as { model . get ( ' name ' , model_id ) } , addressing the most recent message while being aware of the full context. "
2025-09-17 05:49:44 +00:00
if thread_history
else " "
) ,
}
2025-09-24 21:08:36 +00:00
content = f " { user . name if user else ' User ' } : { message_content } "
if images :
content = [
{
" type " : " text " ,
" text " : content ,
} ,
* [
{
" type " : " image_url " ,
" image_url " : {
" url " : image ,
} ,
}
for image in images
] ,
]
2025-09-17 05:49:44 +00:00
form_data = {
" model " : model_id ,
" messages " : [
system_message ,
2025-09-24 21:08:36 +00:00
{ " role " : " user " , " content " : content } ,
2025-09-17 05:49:44 +00:00
] ,
" stream " : False ,
}
res = await generate_chat_completion (
request ,
form_data = form_data ,
user = user ,
)
if res :
2025-10-06 04:29:48 +00:00
if res . get ( " choices " , [ ] ) and len ( res [ " choices " ] ) > 0 :
await update_message_by_id (
channel . id ,
response_message . id ,
MessageForm (
* * {
" content " : res [ " choices " ] [ 0 ] [ " message " ] [ " content " ] ,
" meta " : {
" done " : True ,
} ,
}
) ,
user ,
)
elif res . get ( " error " , None ) :
await update_message_by_id (
channel . id ,
response_message . id ,
MessageForm (
* * {
" content " : f " Error: { res [ ' error ' ] } " ,
" meta " : {
" done " : True ,
} ,
}
) ,
user ,
)
2025-09-17 05:49:44 +00:00
except Exception as e :
log . info ( e )
pass
return True
async def new_message_handler (
request : Request , id : str , form_data : MessageForm , user = Depends ( get_verified_user )
2024-12-22 10:42:19 +00:00
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if user . role != " admin " and not has_access (
user . id , type = " write " , access_control = channel . access_control , strict = False
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-22 10:42:19 +00:00
try :
message = Messages . insert_new_message ( form_data , channel . id , user . id )
2024-12-23 02:40:01 +00:00
if message :
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
members = Channels . get_members_by_channel_id ( channel . id )
for member in members :
if not member . is_active :
Channels . update_member_active_status (
channel . id , member . user_id , True
)
2025-09-27 09:05:12 +00:00
message = Messages . get_message_by_id ( message . id )
2024-12-25 07:53:25 +00:00
event_data = {
" channel_id " : channel . id ,
" message_id " : message . id ,
" data " : {
" type " : " message " ,
2025-11-28 15:45:48 +00:00
" data " : { " temp_id " : form_data . temp_id , * * message . model_dump ( ) } ,
2024-12-23 02:40:01 +00:00
} ,
2024-12-25 07:53:25 +00:00
" user " : UserNameResponse ( * * user . model_dump ( ) ) . model_dump ( ) ,
" channel " : channel . model_dump ( ) ,
}
await sio . emit (
2025-10-02 09:09:17 +00:00
" events:channel " ,
2024-12-25 07:53:25 +00:00
event_data ,
2024-12-23 02:40:01 +00:00
to = f " channel: { channel . id } " ,
)
2024-12-31 10:05:11 +00:00
if message . parent_id :
# If this message is a reply, emit to the parent message as well
parent_message = Messages . get_message_by_id ( message . parent_id )
if parent_message :
await sio . emit (
2025-10-02 09:09:17 +00:00
" events:channel " ,
2024-12-31 10:05:11 +00:00
{
" channel_id " : channel . id ,
" message_id " : parent_message . id ,
" data " : {
" type " : " message:reply " ,
2025-09-27 09:05:12 +00:00
" data " : parent_message . model_dump ( ) ,
2024-12-31 10:05:11 +00:00
} ,
" user " : UserNameResponse ( * * user . model_dump ( ) ) . model_dump ( ) ,
" channel " : channel . model_dump ( ) ,
} ,
to = f " channel: { channel . id } " ,
)
2025-09-27 09:05:12 +00:00
return message , channel
else :
raise Exception ( " Error creating message " )
2025-09-17 05:49:44 +00:00
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-31 10:05:11 +00:00
2024-12-25 07:53:25 +00:00
2025-09-17 05:49:44 +00:00
@router.post ( " / {id} /messages/post " , response_model = Optional [ MessageModel ] )
async def post_new_message (
request : Request ,
id : str ,
form_data : MessageForm ,
background_tasks : BackgroundTasks ,
user = Depends ( get_verified_user ) ,
) :
try :
message , channel = await new_message_handler ( request , id , form_data , user )
active_user_ids = get_user_ids_from_room ( f " channel: { channel . id } " )
async def background_handler ( ) :
await model_response_handler ( request , channel , message , user )
await send_notification (
request . app . state . WEBUI_NAME ,
request . app . state . config . WEBUI_URL ,
channel ,
message ,
active_user_ids ,
)
background_tasks . add_task ( background_handler )
2025-09-17 03:53:54 +00:00
2025-09-17 05:49:44 +00:00
return message
2024-12-25 07:53:25 +00:00
2025-09-17 05:49:44 +00:00
except HTTPException as e :
raise e
2024-12-22 10:42:19 +00:00
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-23 07:53:45 +00:00
2024-12-31 10:05:11 +00:00
############################
# GetChannelMessage
############################
@router.get ( " / {id} /messages/ {message_id} " , response_model = Optional [ MessageUserResponse ] )
async def get_channel_message (
id : str , message_id : str , user = Depends ( get_verified_user )
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if user . role != " admin " and not has_access (
user . id , type = " read " , access_control = channel . access_control
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-31 10:05:11 +00:00
message = Messages . get_message_by_id ( message_id )
if not message :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if message . channel_id != id :
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
return MessageUserResponse (
* * {
* * message . model_dump ( ) ,
" user " : UserNameResponse (
* * Users . get_user_by_id ( message . user_id ) . model_dump ( )
) ,
}
)
2025-11-28 14:58:44 +00:00
############################
# PinChannelMessage
############################
class PinMessageForm ( BaseModel ) :
is_pinned : bool
@router.post (
" / {id} /messages/ {message_id} /pin " , response_model = Optional [ MessageUserResponse ]
)
async def pin_channel_message (
id : str , message_id : str , form_data : PinMessageForm , user = Depends ( get_verified_user )
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-28 14:58:44 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if user . role != " admin " and not has_access (
user . id , type = " read " , access_control = channel . access_control
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
message = Messages . get_message_by_id ( message_id )
if not message :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if message . channel_id != id :
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
try :
Messages . update_is_pinned_by_id ( message_id , form_data . is_pinned , user . id )
message = Messages . get_message_by_id ( message_id )
return MessageUserResponse (
* * {
* * message . model_dump ( ) ,
" user " : UserNameResponse (
* * Users . get_user_by_id ( message . user_id ) . model_dump ( )
) ,
}
)
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-31 08:51:43 +00:00
############################
# GetChannelThreadMessages
############################
@router.get (
" / {id} /messages/ {message_id} /thread " , response_model = list [ MessageUserResponse ]
)
async def get_channel_thread_messages (
id : str ,
message_id : str ,
skip : int = 0 ,
limit : int = 50 ,
user = Depends ( get_verified_user ) ,
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if user . role != " admin " and not has_access (
user . id , type = " read " , access_control = channel . access_control
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-31 08:51:43 +00:00
message_list = Messages . get_messages_by_parent_id ( id , message_id , skip , limit )
users = { }
messages = [ ]
for message in message_list :
if message . user_id not in users :
user = Users . get_user_by_id ( message . user_id )
users [ message . user_id ] = user
messages . append (
MessageUserResponse (
* * {
* * message . model_dump ( ) ,
2024-12-31 10:05:11 +00:00
" reply_count " : 0 ,
" latest_reply_at " : None ,
2024-12-31 08:51:43 +00:00
" reactions " : Messages . get_reactions_by_message_id ( message . id ) ,
" user " : UserNameResponse ( * * users [ message . user_id ] . model_dump ( ) ) ,
}
)
)
return messages
2024-12-23 07:53:45 +00:00
############################
# UpdateMessageById
############################
@router.post (
" / {id} /messages/ {message_id} /update " , response_model = Optional [ MessageModel ]
)
async def update_message_by_id (
id : str , message_id : str , form_data : MessageForm , user = Depends ( get_verified_user )
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
message = Messages . get_message_by_id ( message_id )
if not message :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if message . channel_id != id :
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if (
user . role != " admin "
and message . user_id != user . id
and not has_access (
user . id , type = " read " , access_control = channel . access_control
)
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2025-07-20 11:17:17 +00:00
2024-12-23 07:53:45 +00:00
try :
message = Messages . update_message_by_id ( message_id , form_data )
2024-12-31 07:12:50 +00:00
message = Messages . get_message_by_id ( message_id )
2024-12-23 07:53:45 +00:00
if message :
await sio . emit (
2025-10-02 09:09:17 +00:00
" events:channel " ,
2024-12-23 07:53:45 +00:00
{
" channel_id " : channel . id ,
" message_id " : message . id ,
" data " : {
" type " : " message:update " ,
2025-09-27 09:05:12 +00:00
" data " : message . model_dump ( ) ,
2024-12-23 07:53:45 +00:00
} ,
2024-12-25 01:25:59 +00:00
" user " : UserNameResponse ( * * user . model_dump ( ) ) . model_dump ( ) ,
" channel " : channel . model_dump ( ) ,
2024-12-23 07:53:45 +00:00
} ,
to = f " channel: { channel . id } " ,
)
return MessageModel ( * * message . model_dump ( ) )
except Exception as e :
log . exception ( e )
2024-12-31 07:06:34 +00:00
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
############################
# AddReactionToMessage
############################
class ReactionForm ( BaseModel ) :
name : str
@router.post ( " / {id} /messages/ {message_id} /reactions/add " , response_model = bool )
async def add_reaction_to_message (
id : str , message_id : str , form_data : ReactionForm , user = Depends ( get_verified_user )
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if user . role != " admin " and not has_access (
user . id , type = " write " , access_control = channel . access_control , strict = False
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-31 07:06:34 +00:00
message = Messages . get_message_by_id ( message_id )
if not message :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if message . channel_id != id :
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
try :
Messages . add_reaction_to_message ( message_id , user . id , form_data . name )
message = Messages . get_message_by_id ( message_id )
2024-12-31 10:05:11 +00:00
2024-12-31 07:06:34 +00:00
await sio . emit (
2025-10-02 09:09:17 +00:00
" events:channel " ,
2024-12-31 07:06:34 +00:00
{
" channel_id " : channel . id ,
" message_id " : message . id ,
" data " : {
2024-12-31 10:05:11 +00:00
" type " : " message:reaction:add " ,
2024-12-31 07:06:34 +00:00
" data " : {
* * message . model_dump ( ) ,
" name " : form_data . name ,
} ,
} ,
" user " : UserNameResponse ( * * user . model_dump ( ) ) . model_dump ( ) ,
" channel " : channel . model_dump ( ) ,
} ,
to = f " channel: { channel . id } " ,
)
return True
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
############################
# RemoveReactionById
############################
@router.post ( " / {id} /messages/ {message_id} /reactions/remove " , response_model = bool )
async def remove_reaction_by_id_and_user_id_and_name (
id : str , message_id : str , form_data : ReactionForm , user = Depends ( get_verified_user )
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if user . role != " admin " and not has_access (
user . id , type = " write " , access_control = channel . access_control , strict = False
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2024-12-31 07:06:34 +00:00
message = Messages . get_message_by_id ( message_id )
if not message :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if message . channel_id != id :
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
try :
Messages . remove_reaction_by_id_and_user_id_and_name (
message_id , user . id , form_data . name
)
message = Messages . get_message_by_id ( message_id )
2024-12-31 07:12:50 +00:00
2024-12-31 07:06:34 +00:00
await sio . emit (
2025-10-02 09:09:17 +00:00
" events:channel " ,
2024-12-31 07:06:34 +00:00
{
" channel_id " : channel . id ,
" message_id " : message . id ,
" data " : {
2024-12-31 10:05:11 +00:00
" type " : " message:reaction:remove " ,
2024-12-31 07:06:34 +00:00
" data " : {
* * message . model_dump ( ) ,
" name " : form_data . name ,
} ,
} ,
" user " : UserNameResponse ( * * user . model_dump ( ) ) . model_dump ( ) ,
" channel " : channel . model_dump ( ) ,
} ,
to = f " channel: { channel . id } " ,
)
return True
except Exception as e :
log . exception ( e )
2024-12-23 07:53:45 +00:00
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
############################
# DeleteMessageById
############################
@router.delete ( " / {id} /messages/ {message_id} /delete " , response_model = bool )
async def delete_message_by_id (
id : str , message_id : str , user = Depends ( get_verified_user )
) :
channel = Channels . get_channel_by_id ( id )
if not channel :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
message = Messages . get_message_by_id ( message_id )
if not message :
raise HTTPException (
status_code = status . HTTP_404_NOT_FOUND , detail = ERROR_MESSAGES . NOT_FOUND
)
if message . channel_id != id :
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)
2025-11-30 13:24:27 +00:00
if channel . type in [ " group " , " dm " ] :
2025-11-27 12:27:32 +00:00
if not Channels . is_user_channel_member ( channel . id , user . id ) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
else :
if (
user . role != " admin "
and message . user_id != user . id
and not has_access (
user . id ,
type = " write " ,
access_control = channel . access_control ,
strict = False ,
)
) :
raise HTTPException (
status_code = status . HTTP_403_FORBIDDEN , detail = ERROR_MESSAGES . DEFAULT ( )
)
2025-07-20 11:17:17 +00:00
2024-12-23 07:53:45 +00:00
try :
Messages . delete_message_by_id ( message_id )
await sio . emit (
2025-10-02 09:09:17 +00:00
" events:channel " ,
2024-12-23 07:53:45 +00:00
{
" channel_id " : channel . id ,
" message_id " : message . id ,
" data " : {
" type " : " message:delete " ,
" data " : {
* * message . model_dump ( ) ,
" user " : UserNameResponse ( * * user . model_dump ( ) ) . model_dump ( ) ,
} ,
} ,
2024-12-25 01:25:59 +00:00
" user " : UserNameResponse ( * * user . model_dump ( ) ) . model_dump ( ) ,
" channel " : channel . model_dump ( ) ,
2024-12-23 07:53:45 +00:00
} ,
to = f " channel: { channel . id } " ,
)
2024-12-31 21:39:03 +00:00
if message . parent_id :
# If this message is a reply, emit to the parent message as well
parent_message = Messages . get_message_by_id ( message . parent_id )
if parent_message :
await sio . emit (
2025-10-02 09:09:17 +00:00
" events:channel " ,
2024-12-31 21:39:03 +00:00
{
" channel_id " : channel . id ,
" message_id " : parent_message . id ,
" data " : {
" type " : " message:reply " ,
2025-09-27 09:05:12 +00:00
" data " : parent_message . model_dump ( ) ,
2024-12-31 21:39:03 +00:00
} ,
" user " : UserNameResponse ( * * user . model_dump ( ) ) . model_dump ( ) ,
" channel " : channel . model_dump ( ) ,
} ,
to = f " channel: { channel . id } " ,
)
2024-12-23 07:53:45 +00:00
return True
except Exception as e :
log . exception ( e )
raise HTTPException (
status_code = status . HTTP_400_BAD_REQUEST , detail = ERROR_MESSAGES . DEFAULT ( )
)