161 lines
4.8 KiB
Rust
161 lines
4.8 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use uuid::Uuid;
|
|
|
|
use crate::event::{RoomInfo, UserInfo, message, reaction};
|
|
use crate::{ChannelBus, ChannelError, ChannelResult};
|
|
|
|
use super::WsHandler;
|
|
|
|
impl WsHandler {
|
|
pub(super) async fn ensure_room_access(
|
|
bus: &ChannelBus,
|
|
user_id: Uuid,
|
|
room: Uuid,
|
|
) -> ChannelResult<()> {
|
|
let rooms = crate::rooms::user_rooms(
|
|
&bus.inner.db,
|
|
&bus.inner.cache,
|
|
&bus.inner.config,
|
|
user_id,
|
|
)
|
|
.await?;
|
|
if rooms.contains(&room) {
|
|
Ok(())
|
|
} else {
|
|
Err(ChannelError::AccessDenied)
|
|
}
|
|
}
|
|
|
|
pub(super) async fn ensure_message_in_room(
|
|
bus: &ChannelBus,
|
|
room: Uuid,
|
|
message: Uuid,
|
|
) -> ChannelResult<()> {
|
|
let exists: Option<(Uuid,)> = db::sqlx::query_as(
|
|
"SELECT id FROM room_message WHERE id = $1 AND room = $2 AND deleted_at IS NULL",
|
|
)
|
|
.bind(message)
|
|
.bind(room)
|
|
.fetch_optional(bus.inner.db.reader())
|
|
.await?;
|
|
exists.map(|_| ()).ok_or(ChannelError::RoomNotFound)
|
|
}
|
|
|
|
pub(super) async fn reaction_groups_for_messages(
|
|
bus: &ChannelBus,
|
|
user_id: Uuid,
|
|
message_ids: &[Uuid],
|
|
) -> ChannelResult<HashMap<Uuid, Vec<reaction::ReactionGroup>>> {
|
|
if message_ids.is_empty() {
|
|
return Ok(HashMap::new());
|
|
}
|
|
|
|
let rows = db::sqlx::query_as::<_, (Uuid, String, Uuid)>(
|
|
"SELECT message, reaction, \"user\" FROM room_reaction \
|
|
WHERE message = ANY($1) ORDER BY created_at ASC",
|
|
)
|
|
.bind(message_ids)
|
|
.fetch_all(bus.inner.db.reader())
|
|
.await?;
|
|
|
|
let user_ids: Vec<Uuid> =
|
|
rows.iter().map(|(_, _, user)| *user).collect();
|
|
let users = bus.lookup_users(&user_ids).await.unwrap_or_default();
|
|
let mut grouped: HashMap<
|
|
Uuid,
|
|
HashMap<String, reaction::ReactionGroup>,
|
|
> = HashMap::new();
|
|
|
|
for (message_id, emoji, reactor) in rows {
|
|
let group = grouped
|
|
.entry(message_id)
|
|
.or_default()
|
|
.entry(emoji.clone())
|
|
.or_insert_with(|| reaction::ReactionGroup {
|
|
emoji: emoji.clone(),
|
|
count: 0,
|
|
reacted_by_me: false,
|
|
users: Vec::new(),
|
|
});
|
|
group.count += 1;
|
|
group.reacted_by_me |= reactor == user_id;
|
|
group.users.push(
|
|
users
|
|
.get(&reactor)
|
|
.cloned()
|
|
.unwrap_or_else(|| UserInfo::unknown(reactor)),
|
|
);
|
|
}
|
|
|
|
Ok(grouped
|
|
.into_iter()
|
|
.map(|(message_id, groups)| {
|
|
(message_id, groups.into_values().collect::<Vec<_>>())
|
|
})
|
|
.collect())
|
|
}
|
|
|
|
pub(super) async fn ensure_workspace_member(
|
|
bus: &ChannelBus,
|
|
user_id: Uuid,
|
|
wk: Uuid,
|
|
) -> ChannelResult<()> {
|
|
let row: Option<(Uuid,)> = db::sqlx::query_as(
|
|
"SELECT wk FROM wk_member WHERE wk = $1 AND \"user\" = $2 AND leave_at IS NULL",
|
|
)
|
|
.bind(wk)
|
|
.bind(user_id)
|
|
.fetch_optional(bus.inner.db.reader())
|
|
.await?;
|
|
row.map(|_| ()).ok_or(ChannelError::AccessDenied)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub(super) fn missed_message_data(
|
|
m: crate::MissedMessage,
|
|
) -> message::MessageNewService {
|
|
message::MessageNewService {
|
|
id: m.message_id,
|
|
seq: m.seq,
|
|
room: RoomInfo::unknown(m.room_id),
|
|
sender_type: "user".to_string(),
|
|
sender: UserInfo::unknown(m.sender_id),
|
|
thread: None,
|
|
in_reply_to: None,
|
|
content: m.content,
|
|
content_type: "text".to_string(),
|
|
pinned: false,
|
|
system_type: None,
|
|
metadata: serde_json::Value::Null,
|
|
thinking_content: None,
|
|
thinking_is_chunked: None,
|
|
send_at: m.send_at,
|
|
reactions: vec![],
|
|
}
|
|
}
|
|
#[allow(dead_code)]
|
|
pub(super) fn message_data(
|
|
m: model::room::RoomMessageModel,
|
|
) -> message::MessageNewService {
|
|
message::MessageNewService {
|
|
id: m.id,
|
|
seq: m.seq,
|
|
room: RoomInfo::unknown(m.room),
|
|
sender_type: "user".to_string(),
|
|
sender: UserInfo::unknown(m.author),
|
|
thread: m.thread,
|
|
in_reply_to: m.parent,
|
|
content: m.content,
|
|
content_type: m.content_type,
|
|
pinned: m.pinned,
|
|
system_type: m.system_type,
|
|
metadata: m.metadata,
|
|
thinking_content: None,
|
|
thinking_is_chunked: None,
|
|
send_at: m.created_at,
|
|
reactions: vec![],
|
|
}
|
|
}
|
|
}
|