channels.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. import json
  2. import logging
  3. from typing import Optional
  4. from fastapi import APIRouter, Depends, HTTPException, Request, status, BackgroundTasks
  5. from pydantic import BaseModel
  6. from open_webui.socket.main import sio, get_user_ids_from_room
  7. from open_webui.models.users import Users, UserNameResponse
  8. from open_webui.models.channels import Channels, ChannelModel, ChannelForm
  9. from open_webui.models.messages import (
  10. Messages,
  11. MessageModel,
  12. MessageResponse,
  13. MessageForm,
  14. )
  15. from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT
  16. from open_webui.constants import ERROR_MESSAGES
  17. from open_webui.env import SRC_LOG_LEVELS
  18. from open_webui.utils.models import (
  19. get_all_models,
  20. get_filtered_models,
  21. )
  22. from open_webui.utils.chat import generate_chat_completion
  23. from open_webui.utils.auth import get_admin_user, get_verified_user
  24. from open_webui.utils.access_control import has_access, get_users_with_access
  25. from open_webui.utils.webhook import post_webhook
  26. from open_webui.utils.channels import extract_mentions, replace_mentions
  27. log = logging.getLogger(__name__)
  28. log.setLevel(SRC_LOG_LEVELS["MODELS"])
  29. router = APIRouter()
  30. ############################
  31. # GetChatList
  32. ############################
  33. @router.get("/", response_model=list[ChannelModel])
  34. async def get_channels(user=Depends(get_verified_user)):
  35. return Channels.get_channels_by_user_id(user.id)
  36. @router.get("/list", response_model=list[ChannelModel])
  37. async def get_all_channels(user=Depends(get_verified_user)):
  38. if user.role == "admin":
  39. return Channels.get_channels()
  40. return Channels.get_channels_by_user_id(user.id)
  41. ############################
  42. # CreateNewChannel
  43. ############################
  44. @router.post("/create", response_model=Optional[ChannelModel])
  45. async def create_new_channel(form_data: ChannelForm, user=Depends(get_admin_user)):
  46. try:
  47. channel = Channels.insert_new_channel(None, form_data, user.id)
  48. return ChannelModel(**channel.model_dump())
  49. except Exception as e:
  50. log.exception(e)
  51. raise HTTPException(
  52. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  53. )
  54. ############################
  55. # GetChannelById
  56. ############################
  57. @router.get("/{id}", response_model=Optional[ChannelModel])
  58. async def get_channel_by_id(id: str, user=Depends(get_verified_user)):
  59. channel = Channels.get_channel_by_id(id)
  60. if not channel:
  61. raise HTTPException(
  62. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  63. )
  64. if user.role != "admin" and not has_access(
  65. user.id, type="read", access_control=channel.access_control
  66. ):
  67. raise HTTPException(
  68. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  69. )
  70. return ChannelModel(**channel.model_dump())
  71. ############################
  72. # UpdateChannelById
  73. ############################
  74. @router.post("/{id}/update", response_model=Optional[ChannelModel])
  75. async def update_channel_by_id(
  76. id: str, form_data: ChannelForm, user=Depends(get_admin_user)
  77. ):
  78. channel = Channels.get_channel_by_id(id)
  79. if not channel:
  80. raise HTTPException(
  81. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  82. )
  83. try:
  84. channel = Channels.update_channel_by_id(id, form_data)
  85. return ChannelModel(**channel.model_dump())
  86. except Exception as e:
  87. log.exception(e)
  88. raise HTTPException(
  89. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  90. )
  91. ############################
  92. # DeleteChannelById
  93. ############################
  94. @router.delete("/{id}/delete", response_model=bool)
  95. async def delete_channel_by_id(id: str, user=Depends(get_admin_user)):
  96. channel = Channels.get_channel_by_id(id)
  97. if not channel:
  98. raise HTTPException(
  99. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  100. )
  101. try:
  102. Channels.delete_channel_by_id(id)
  103. return True
  104. except Exception as e:
  105. log.exception(e)
  106. raise HTTPException(
  107. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  108. )
  109. ############################
  110. # GetChannelMessages
  111. ############################
  112. class MessageUserResponse(MessageResponse):
  113. user: UserNameResponse
  114. @router.get("/{id}/messages", response_model=list[MessageUserResponse])
  115. async def get_channel_messages(
  116. id: str, skip: int = 0, limit: int = 50, user=Depends(get_verified_user)
  117. ):
  118. channel = Channels.get_channel_by_id(id)
  119. if not channel:
  120. raise HTTPException(
  121. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  122. )
  123. if user.role != "admin" and not has_access(
  124. user.id, type="read", access_control=channel.access_control
  125. ):
  126. raise HTTPException(
  127. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  128. )
  129. message_list = Messages.get_messages_by_channel_id(id, skip, limit)
  130. users = {}
  131. messages = []
  132. for message in message_list:
  133. if message.user_id not in users:
  134. user = Users.get_user_by_id(message.user_id)
  135. users[message.user_id] = user
  136. replies = Messages.get_replies_by_message_id(message.id)
  137. latest_reply_at = replies[0].created_at if replies else None
  138. messages.append(
  139. MessageUserResponse(
  140. **{
  141. **message.model_dump(),
  142. "reply_count": len(replies),
  143. "latest_reply_at": latest_reply_at,
  144. "reactions": Messages.get_reactions_by_message_id(message.id),
  145. "user": UserNameResponse(**users[message.user_id].model_dump()),
  146. }
  147. )
  148. )
  149. return messages
  150. ############################
  151. # PostNewMessage
  152. ############################
  153. async def send_notification(name, webui_url, channel, message, active_user_ids):
  154. users = get_users_with_access("read", channel.access_control)
  155. for user in users:
  156. if user.id not in active_user_ids:
  157. if user.settings:
  158. webhook_url = user.settings.ui.get("notifications", {}).get(
  159. "webhook_url", None
  160. )
  161. if webhook_url:
  162. await post_webhook(
  163. name,
  164. webhook_url,
  165. f"#{channel.name} - {webui_url}/channels/{channel.id}\n\n{message.content}",
  166. {
  167. "action": "channel",
  168. "message": message.content,
  169. "title": channel.name,
  170. "url": f"{webui_url}/channels/{channel.id}",
  171. },
  172. )
  173. return True
  174. async def model_response_handler(request, channel, message, user):
  175. MODELS = {
  176. model["id"]: model
  177. for model in get_filtered_models(await get_all_models(request, user=user), user)
  178. }
  179. mentions = extract_mentions(message.content)
  180. message_content = replace_mentions(message.content)
  181. # check if any of the mentions are models
  182. model_mentions = [mention for mention in mentions if mention["id_type"] == "M"]
  183. if not model_mentions:
  184. return False
  185. for mention in model_mentions:
  186. model_id = mention["id"]
  187. model = MODELS.get(model_id, None)
  188. if model:
  189. try:
  190. # reverse to get in chronological order
  191. thread_messages = Messages.get_messages_by_parent_id(
  192. channel.id,
  193. message.parent_id if message.parent_id else message.id,
  194. )[::-1]
  195. response_message, channel = await new_message_handler(
  196. request,
  197. channel.id,
  198. MessageForm(
  199. **{
  200. "parent_id": (
  201. message.parent_id if message.parent_id else message.id
  202. ),
  203. "content": f"",
  204. "data": {},
  205. "meta": {
  206. "model_id": model_id,
  207. "model_name": model.get("name", model_id),
  208. },
  209. }
  210. ),
  211. user,
  212. )
  213. thread_history = []
  214. message_users = {}
  215. for thread_message in thread_messages:
  216. message_user = None
  217. if thread_message.user_id not in message_users:
  218. message_user = Users.get_user_by_id(thread_message.user_id)
  219. message_users[thread_message.user_id] = message_user
  220. else:
  221. message_user = message_users[thread_message.user_id]
  222. if thread_message.meta and thread_message.meta.get(
  223. "model_id", None
  224. ):
  225. # If the message was sent by a model, use the model name
  226. message_model_id = thread_message.meta.get("model_id", None)
  227. message_model = MODELS.get(message_model_id, None)
  228. username = (
  229. message_model.get("name", message_model_id)
  230. if message_model
  231. else message_model_id
  232. )
  233. else:
  234. username = message_user.name if message_user else "Unknown"
  235. thread_history.append(
  236. f"{username}: {replace_mentions(thread_message.content)}"
  237. )
  238. system_message = {
  239. "role": "system",
  240. "content": f"You are {model.get('name', model_id)}, an AI assistant participating in a threaded conversation. Be helpful, concise, and conversational."
  241. + (
  242. f"Here's the thread history:\n\n{''.join([f'{msg}' for msg in thread_history])}\n\nContinue the conversation naturally, addressing the most recent message while being aware of the full context."
  243. if thread_history
  244. else ""
  245. ),
  246. }
  247. form_data = {
  248. "model": model_id,
  249. "messages": [
  250. system_message,
  251. {
  252. "role": "user",
  253. "content": f"{user.name if user else 'User'}: {message_content}",
  254. },
  255. ],
  256. "stream": False,
  257. }
  258. res = await generate_chat_completion(
  259. request,
  260. form_data=form_data,
  261. user=user,
  262. )
  263. if res:
  264. await update_message_by_id(
  265. channel.id,
  266. response_message.id,
  267. MessageForm(
  268. **{
  269. "content": res["choices"][0]["message"]["content"],
  270. "meta": {
  271. "done": True,
  272. },
  273. }
  274. ),
  275. user,
  276. )
  277. except Exception as e:
  278. log.info(e)
  279. pass
  280. return True
  281. async def new_message_handler(
  282. request: Request, id: str, form_data: MessageForm, user=Depends(get_verified_user)
  283. ):
  284. channel = Channels.get_channel_by_id(id)
  285. if not channel:
  286. raise HTTPException(
  287. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  288. )
  289. if user.role != "admin" and not has_access(
  290. user.id, type="read", access_control=channel.access_control
  291. ):
  292. raise HTTPException(
  293. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  294. )
  295. try:
  296. message = Messages.insert_new_message(form_data, channel.id, user.id)
  297. if message:
  298. event_data = {
  299. "channel_id": channel.id,
  300. "message_id": message.id,
  301. "data": {
  302. "type": "message",
  303. "data": MessageUserResponse(
  304. **{
  305. **message.model_dump(),
  306. "reply_count": 0,
  307. "latest_reply_at": None,
  308. "reactions": Messages.get_reactions_by_message_id(
  309. message.id
  310. ),
  311. "user": UserNameResponse(**user.model_dump()),
  312. }
  313. ).model_dump(),
  314. },
  315. "user": UserNameResponse(**user.model_dump()).model_dump(),
  316. "channel": channel.model_dump(),
  317. }
  318. await sio.emit(
  319. "channel-events",
  320. event_data,
  321. to=f"channel:{channel.id}",
  322. )
  323. if message.parent_id:
  324. # If this message is a reply, emit to the parent message as well
  325. parent_message = Messages.get_message_by_id(message.parent_id)
  326. if parent_message:
  327. await sio.emit(
  328. "channel-events",
  329. {
  330. "channel_id": channel.id,
  331. "message_id": parent_message.id,
  332. "data": {
  333. "type": "message:reply",
  334. "data": MessageUserResponse(
  335. **{
  336. **parent_message.model_dump(),
  337. "user": UserNameResponse(
  338. **Users.get_user_by_id(
  339. parent_message.user_id
  340. ).model_dump()
  341. ),
  342. }
  343. ).model_dump(),
  344. },
  345. "user": UserNameResponse(**user.model_dump()).model_dump(),
  346. "channel": channel.model_dump(),
  347. },
  348. to=f"channel:{channel.id}",
  349. )
  350. return MessageModel(**message.model_dump()), channel
  351. except Exception as e:
  352. log.exception(e)
  353. raise HTTPException(
  354. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  355. )
  356. @router.post("/{id}/messages/post", response_model=Optional[MessageModel])
  357. async def post_new_message(
  358. request: Request,
  359. id: str,
  360. form_data: MessageForm,
  361. background_tasks: BackgroundTasks,
  362. user=Depends(get_verified_user),
  363. ):
  364. try:
  365. message, channel = await new_message_handler(request, id, form_data, user)
  366. active_user_ids = get_user_ids_from_room(f"channel:{channel.id}")
  367. async def background_handler():
  368. await model_response_handler(request, channel, message, user)
  369. await send_notification(
  370. request.app.state.WEBUI_NAME,
  371. request.app.state.config.WEBUI_URL,
  372. channel,
  373. message,
  374. active_user_ids,
  375. )
  376. background_tasks.add_task(background_handler)
  377. return message
  378. except HTTPException as e:
  379. raise e
  380. except Exception as e:
  381. log.exception(e)
  382. raise HTTPException(
  383. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  384. )
  385. ############################
  386. # GetChannelMessage
  387. ############################
  388. @router.get("/{id}/messages/{message_id}", response_model=Optional[MessageUserResponse])
  389. async def get_channel_message(
  390. id: str, message_id: str, user=Depends(get_verified_user)
  391. ):
  392. channel = Channels.get_channel_by_id(id)
  393. if not channel:
  394. raise HTTPException(
  395. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  396. )
  397. if user.role != "admin" and not has_access(
  398. user.id, type="read", access_control=channel.access_control
  399. ):
  400. raise HTTPException(
  401. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  402. )
  403. message = Messages.get_message_by_id(message_id)
  404. if not message:
  405. raise HTTPException(
  406. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  407. )
  408. if message.channel_id != id:
  409. raise HTTPException(
  410. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  411. )
  412. return MessageUserResponse(
  413. **{
  414. **message.model_dump(),
  415. "user": UserNameResponse(
  416. **Users.get_user_by_id(message.user_id).model_dump()
  417. ),
  418. }
  419. )
  420. ############################
  421. # GetChannelThreadMessages
  422. ############################
  423. @router.get(
  424. "/{id}/messages/{message_id}/thread", response_model=list[MessageUserResponse]
  425. )
  426. async def get_channel_thread_messages(
  427. id: str,
  428. message_id: str,
  429. skip: int = 0,
  430. limit: int = 50,
  431. user=Depends(get_verified_user),
  432. ):
  433. channel = Channels.get_channel_by_id(id)
  434. if not channel:
  435. raise HTTPException(
  436. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  437. )
  438. if user.role != "admin" and not has_access(
  439. user.id, type="read", access_control=channel.access_control
  440. ):
  441. raise HTTPException(
  442. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  443. )
  444. message_list = Messages.get_messages_by_parent_id(id, message_id, skip, limit)
  445. users = {}
  446. messages = []
  447. for message in message_list:
  448. if message.user_id not in users:
  449. user = Users.get_user_by_id(message.user_id)
  450. users[message.user_id] = user
  451. messages.append(
  452. MessageUserResponse(
  453. **{
  454. **message.model_dump(),
  455. "reply_count": 0,
  456. "latest_reply_at": None,
  457. "reactions": Messages.get_reactions_by_message_id(message.id),
  458. "user": UserNameResponse(**users[message.user_id].model_dump()),
  459. }
  460. )
  461. )
  462. return messages
  463. ############################
  464. # UpdateMessageById
  465. ############################
  466. @router.post(
  467. "/{id}/messages/{message_id}/update", response_model=Optional[MessageModel]
  468. )
  469. async def update_message_by_id(
  470. id: str, message_id: str, form_data: MessageForm, user=Depends(get_verified_user)
  471. ):
  472. channel = Channels.get_channel_by_id(id)
  473. if not channel:
  474. raise HTTPException(
  475. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  476. )
  477. message = Messages.get_message_by_id(message_id)
  478. if not message:
  479. raise HTTPException(
  480. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  481. )
  482. if message.channel_id != id:
  483. raise HTTPException(
  484. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  485. )
  486. if (
  487. user.role != "admin"
  488. and message.user_id != user.id
  489. and not has_access(user.id, type="read", access_control=channel.access_control)
  490. ):
  491. raise HTTPException(
  492. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  493. )
  494. try:
  495. message = Messages.update_message_by_id(message_id, form_data)
  496. message = Messages.get_message_by_id(message_id)
  497. if message:
  498. await sio.emit(
  499. "channel-events",
  500. {
  501. "channel_id": channel.id,
  502. "message_id": message.id,
  503. "data": {
  504. "type": "message:update",
  505. "data": MessageUserResponse(
  506. **{
  507. **message.model_dump(),
  508. "user": UserNameResponse(
  509. **user.model_dump()
  510. ).model_dump(),
  511. }
  512. ).model_dump(),
  513. },
  514. "user": UserNameResponse(**user.model_dump()).model_dump(),
  515. "channel": channel.model_dump(),
  516. },
  517. to=f"channel:{channel.id}",
  518. )
  519. return MessageModel(**message.model_dump())
  520. except Exception as e:
  521. log.exception(e)
  522. raise HTTPException(
  523. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  524. )
  525. ############################
  526. # AddReactionToMessage
  527. ############################
  528. class ReactionForm(BaseModel):
  529. name: str
  530. @router.post("/{id}/messages/{message_id}/reactions/add", response_model=bool)
  531. async def add_reaction_to_message(
  532. id: str, message_id: str, form_data: ReactionForm, user=Depends(get_verified_user)
  533. ):
  534. channel = Channels.get_channel_by_id(id)
  535. if not channel:
  536. raise HTTPException(
  537. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  538. )
  539. if user.role != "admin" and not has_access(
  540. user.id, type="read", access_control=channel.access_control
  541. ):
  542. raise HTTPException(
  543. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  544. )
  545. message = Messages.get_message_by_id(message_id)
  546. if not message:
  547. raise HTTPException(
  548. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  549. )
  550. if message.channel_id != id:
  551. raise HTTPException(
  552. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  553. )
  554. try:
  555. Messages.add_reaction_to_message(message_id, user.id, form_data.name)
  556. message = Messages.get_message_by_id(message_id)
  557. await sio.emit(
  558. "channel-events",
  559. {
  560. "channel_id": channel.id,
  561. "message_id": message.id,
  562. "data": {
  563. "type": "message:reaction:add",
  564. "data": {
  565. **message.model_dump(),
  566. "user": UserNameResponse(
  567. **Users.get_user_by_id(message.user_id).model_dump()
  568. ).model_dump(),
  569. "name": form_data.name,
  570. },
  571. },
  572. "user": UserNameResponse(**user.model_dump()).model_dump(),
  573. "channel": channel.model_dump(),
  574. },
  575. to=f"channel:{channel.id}",
  576. )
  577. return True
  578. except Exception as e:
  579. log.exception(e)
  580. raise HTTPException(
  581. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  582. )
  583. ############################
  584. # RemoveReactionById
  585. ############################
  586. @router.post("/{id}/messages/{message_id}/reactions/remove", response_model=bool)
  587. async def remove_reaction_by_id_and_user_id_and_name(
  588. id: str, message_id: str, form_data: ReactionForm, user=Depends(get_verified_user)
  589. ):
  590. channel = Channels.get_channel_by_id(id)
  591. if not channel:
  592. raise HTTPException(
  593. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  594. )
  595. if user.role != "admin" and not has_access(
  596. user.id, type="read", access_control=channel.access_control
  597. ):
  598. raise HTTPException(
  599. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  600. )
  601. message = Messages.get_message_by_id(message_id)
  602. if not message:
  603. raise HTTPException(
  604. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  605. )
  606. if message.channel_id != id:
  607. raise HTTPException(
  608. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  609. )
  610. try:
  611. Messages.remove_reaction_by_id_and_user_id_and_name(
  612. message_id, user.id, form_data.name
  613. )
  614. message = Messages.get_message_by_id(message_id)
  615. await sio.emit(
  616. "channel-events",
  617. {
  618. "channel_id": channel.id,
  619. "message_id": message.id,
  620. "data": {
  621. "type": "message:reaction:remove",
  622. "data": {
  623. **message.model_dump(),
  624. "user": UserNameResponse(
  625. **Users.get_user_by_id(message.user_id).model_dump()
  626. ).model_dump(),
  627. "name": form_data.name,
  628. },
  629. },
  630. "user": UserNameResponse(**user.model_dump()).model_dump(),
  631. "channel": channel.model_dump(),
  632. },
  633. to=f"channel:{channel.id}",
  634. )
  635. return True
  636. except Exception as e:
  637. log.exception(e)
  638. raise HTTPException(
  639. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  640. )
  641. ############################
  642. # DeleteMessageById
  643. ############################
  644. @router.delete("/{id}/messages/{message_id}/delete", response_model=bool)
  645. async def delete_message_by_id(
  646. id: str, message_id: str, user=Depends(get_verified_user)
  647. ):
  648. channel = Channels.get_channel_by_id(id)
  649. if not channel:
  650. raise HTTPException(
  651. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  652. )
  653. message = Messages.get_message_by_id(message_id)
  654. if not message:
  655. raise HTTPException(
  656. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  657. )
  658. if message.channel_id != id:
  659. raise HTTPException(
  660. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  661. )
  662. if (
  663. user.role != "admin"
  664. and message.user_id != user.id
  665. and not has_access(user.id, type="read", access_control=channel.access_control)
  666. ):
  667. raise HTTPException(
  668. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  669. )
  670. try:
  671. Messages.delete_message_by_id(message_id)
  672. await sio.emit(
  673. "channel-events",
  674. {
  675. "channel_id": channel.id,
  676. "message_id": message.id,
  677. "data": {
  678. "type": "message:delete",
  679. "data": {
  680. **message.model_dump(),
  681. "user": UserNameResponse(**user.model_dump()).model_dump(),
  682. },
  683. },
  684. "user": UserNameResponse(**user.model_dump()).model_dump(),
  685. "channel": channel.model_dump(),
  686. },
  687. to=f"channel:{channel.id}",
  688. )
  689. if message.parent_id:
  690. # If this message is a reply, emit to the parent message as well
  691. parent_message = Messages.get_message_by_id(message.parent_id)
  692. if parent_message:
  693. await sio.emit(
  694. "channel-events",
  695. {
  696. "channel_id": channel.id,
  697. "message_id": parent_message.id,
  698. "data": {
  699. "type": "message:reply",
  700. "data": MessageUserResponse(
  701. **{
  702. **parent_message.model_dump(),
  703. "user": UserNameResponse(
  704. **Users.get_user_by_id(
  705. parent_message.user_id
  706. ).model_dump()
  707. ),
  708. }
  709. ).model_dump(),
  710. },
  711. "user": UserNameResponse(**user.model_dump()).model_dump(),
  712. "channel": channel.model_dump(),
  713. },
  714. to=f"channel:{channel.id}",
  715. )
  716. return True
  717. except Exception as e:
  718. log.exception(e)
  719. raise HTTPException(
  720. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  721. )