1
0

channels.py 28 KB

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