VNOX-Server/gateway/src/handler/channel/join.rs

127 lines
3.7 KiB
Rust

use anyhow::Result;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::info;
use crate::{
domain::{channels, session},
net::{
io,
state::{BroadcastMsg, State},
},
proto::{
self, ChannelStatePayload, ChatHistoryPayload, MemberInfo, PacketId, SessionCrypto,
UserJoinPayload, encode_packet, to_payload,
},
};
use super::{broadcast_leave, set_channel};
const HISTORY_LIMIT: i64 = 50;
pub async fn join(
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
channel_id: &str,
crypto: &SessionCrypto,
state: &State,
) -> Result<()> {
let prev_channel = session::get(&state.sessions, session_id)
.await
.and_then(|s| s.channel_id);
if prev_channel.as_deref() == Some(channel_id) {
return Ok(());
}
if let Some(prev) = prev_channel {
channels::leave(&state.channels, &prev, session_id).await;
broadcast_leave(state, &prev, session_id).await;
}
let ch = match channels::get_channel(&state.channels, channel_id).await {
Some(c) => c,
None => {
io::send_encrypted(
stream,
PacketId::Error,
seq,
&to_payload(&proto::ErrorPayload {
code: proto::ErrorCode::ChannelNotFound as u32,
message: "not found".into(),
}),
crypto,
)
.await?;
return Ok(());
}
};
channels::join(&state.channels, channel_id, session_id).await;
set_channel(state, session_id, Some(channel_id.into())).await;
let mut members = Vec::new();
for sid in channels::members(&state.channels, channel_id).await {
if let Some(s) = session::get(&state.sessions, &sid).await {
members.push(MemberInfo {
user_id: s.user_id.clone(),
nickname: s.nickname.clone(),
in_voice: ch.kind == channels::ChannelKind::Voice,
});
}
}
let sp = ChannelStatePayload {
channel_id: ch.id.clone(),
channel_name: ch.name.clone(),
kind: ch.kind.as_str().into(),
members,
voice_endpoint: state.config.voice.bind.clone(),
};
io::send_encrypted(
stream,
PacketId::ChannelState,
seq,
&to_payload(&sp),
crypto,
)
.await?;
let history = state.storage.get_history(channel_id, HISTORY_LIMIT).await?;
if !history.is_empty() {
let hp = ChatHistoryPayload {
channel_id: channel_id.into(),
messages: history,
};
io::send_encrypted(stream, PacketId::ChatHistory, seq, &to_payload(&hp), crypto).await?;
}
if let Some(sess) = session::get(&state.sessions, session_id).await {
let jp = UserJoinPayload {
channel_id: channel_id.into(),
user_id: sess.user_id.clone(),
nickname: sess.nickname.clone(),
};
let _ = state.broadcast.send(BroadcastMsg {
channel_id: Some(channel_id.into()),
exclude_session: Some(session_id.into()),
target_session_id: None,
data: encode_packet(PacketId::UserJoin, 0, &to_payload(&jp)),
});
}
if let Some(tx) = &state.voice_member_tx
&& let Some(sess) = session::get(&state.sessions, session_id).await
{
let event = serde_json::json!({
"type": "joined",
"channel_id": channel_id,
"session_id": session_id,
"user_id": sess.user_id,
});
let _ = tx.send(event.to_string());
}
info!("session {} joined {channel_id}", &session_id[..8]);
Ok(())
}