forked from jmug/stoatchat
feat: switch to RabbitMQ streams from Redis Pub/Sub
refactor: redo the bonfire client structure
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
use async_std::{net::TcpStream, sync::Mutex};
|
||||
use async_tungstenite::WebSocketStream;
|
||||
use futures::{join, SinkExt, StreamExt, TryStreamExt};
|
||||
use revolt_config::report_internal_error;
|
||||
use revolt_database::{
|
||||
events::{client::EventV1, server::ClientMessage},
|
||||
iso8601_timestamp::Timestamp,
|
||||
Database, User, UserHint,
|
||||
};
|
||||
use revolt_presence::{create_session, delete_session};
|
||||
use revolt_result::create_error;
|
||||
|
||||
use crate::{
|
||||
client::{
|
||||
subscriber::client_subscriber,
|
||||
worker::{client_worker, WorkerRef},
|
||||
},
|
||||
config::ProtocolConfiguration,
|
||||
events::state::State,
|
||||
};
|
||||
|
||||
/// Core event loop of gateway clients
|
||||
pub async fn client_core(
|
||||
db: &'static Database,
|
||||
ws: WebSocketStream<TcpStream>,
|
||||
mut config: ProtocolConfiguration,
|
||||
) {
|
||||
// Split the socket for simultaneously read and write.
|
||||
let (mut write, mut read) = ws.split();
|
||||
|
||||
// If the user has not provided authentication, request information.
|
||||
if config.get_session_token().is_none() {
|
||||
while let Ok(Some(message)) = read.try_next().await {
|
||||
if let Ok(ClientMessage::Authenticate { token }) = config.decode(&message) {
|
||||
config.set_session_token(token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to authenticate the user.
|
||||
let Some(token) = config.get_session_token().as_ref() else {
|
||||
write
|
||||
.send(config.encode(&EventV1::Error {
|
||||
data: create_error!(InvalidSession),
|
||||
}))
|
||||
.await
|
||||
.ok();
|
||||
return;
|
||||
};
|
||||
|
||||
let (user, session_id) = match User::from_token(db, token, UserHint::Any).await {
|
||||
Ok(user) => user,
|
||||
Err(err) => {
|
||||
write
|
||||
.send(config.encode(&EventV1::Error { data: err }))
|
||||
.await
|
||||
.ok();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
info!(
|
||||
"Authenticated user {}#{}",
|
||||
user.username, user.discriminator
|
||||
);
|
||||
|
||||
db.update_session_last_seen(&session_id, Timestamp::now_utc())
|
||||
.await
|
||||
.ok();
|
||||
|
||||
// Create local state.
|
||||
let mut state = State::from(user, session_id);
|
||||
let user_id = state.cache.user_id.clone();
|
||||
|
||||
// Notify socket we have authenticated.
|
||||
if report_internal_error!(write.send(config.encode(&EventV1::Authenticated)).await).is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Download required data to local cache and send Ready payload.
|
||||
let ready_payload = match report_internal_error!(
|
||||
state
|
||||
.generate_ready_payload(db, config.get_ready_payload_fields())
|
||||
.await
|
||||
) {
|
||||
Ok(ready_payload) => ready_payload,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
if report_internal_error!(write.send(config.encode(&ready_payload)).await).is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create presence session.
|
||||
let (first_session, session_id) = create_session(&user_id, 0).await;
|
||||
|
||||
// If this was the first session, notify other users that we just went online.
|
||||
if first_session {
|
||||
state.broadcast_presence_change(true).await;
|
||||
}
|
||||
|
||||
{
|
||||
let worker_ref = WorkerRef::from(&state);
|
||||
let write = Mutex::new(write);
|
||||
let (reload, reloaded) = async_channel::bounded(1);
|
||||
let (cancel_1, cancelled_1) = async_channel::bounded(1);
|
||||
let (cancel_2, cancelled_2) = async_channel::bounded(1);
|
||||
|
||||
join!(
|
||||
async {
|
||||
client_subscriber(&write, cancelled_1, reloaded, &config, db, &mut state).await;
|
||||
cancel_2.send(()).await.ok();
|
||||
},
|
||||
async {
|
||||
client_worker(read, &write, cancelled_2, reload, &config, worker_ref).await;
|
||||
cancel_1.send(()).await.ok();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Clean up presence session.
|
||||
let last_session = delete_session(&user_id, session_id).await;
|
||||
|
||||
// If this was the last session, notify other users that we just went offline.
|
||||
if last_session {
|
||||
state.broadcast_presence_change(false).await;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user