74 lines
2.8 KiB
Rust
74 lines
2.8 KiB
Rust
|
use std::sync::Arc;
|
||
|
|
||
|
// TODO im so angry tokio-tungstenite makes me import this!
|
||
|
use futures_util::StreamExt;
|
||
|
use tokio::sync::RwLock;
|
||
|
use tokio_tungstenite::tungstenite::Message;
|
||
|
|
||
|
use crate::proto::{http::RegisterResponse, ws::{ChatEvent, ChatEventInner}};
|
||
|
|
||
|
pub type Timeline = Arc<RwLock<Vec<String>>>;
|
||
|
|
||
|
#[async_trait::async_trait]
|
||
|
pub trait PushableTimeline {
|
||
|
async fn push(&self, msg: String);
|
||
|
}
|
||
|
|
||
|
#[async_trait::async_trait]
|
||
|
impl PushableTimeline for Timeline {
|
||
|
async fn push(&self, msg: String) {
|
||
|
self.write().await.push(msg)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub async fn worker(server: String, timeline: Timeline) {
|
||
|
timeline.push("[ ] connecting...".to_string()).await;
|
||
|
let registration : RegisterResponse = reqwest::Client::new()
|
||
|
.post(format!("https://{server}/api/chat/register"))
|
||
|
.send()
|
||
|
.await
|
||
|
.expect("failed sending registration request")
|
||
|
.json()
|
||
|
.await
|
||
|
.expect("failed parsing registration response");
|
||
|
|
||
|
timeline.push(format!("registered to chat: {registration:?}")).await;
|
||
|
|
||
|
// TODO use url crate?
|
||
|
let ws_url = format!("wss://{server}/ws?accessToken={}", registration.access_token);
|
||
|
|
||
|
timeline.push(format!("connecting to {ws_url}")).await;
|
||
|
|
||
|
match tokio_tungstenite::connect_async(ws_url).await {
|
||
|
Err(e) => timeline.push(format!("[!] failed connecting: {e}")).await,
|
||
|
Ok((mut stream, _)) => while let Some(next) = stream.next().await {
|
||
|
match next {
|
||
|
Err(e) => timeline.push(format!("[!] error receiving chat message: {e}")).await,
|
||
|
Ok(msg) => match msg {
|
||
|
Message::Binary(_) => timeline.push("[!] received unexpected binary payload".to_string()).await,
|
||
|
Message::Frame(_) => timeline.push("[!] received unexpected raw frame".to_string()).await,
|
||
|
Message::Ping(_) | tokio_tungstenite::tungstenite::Message::Pong(_) => {}, // ignore
|
||
|
Message::Close(_) => timeline.push("[x] stream is closing".to_string()).await,
|
||
|
Message::Text(payload) => for line in payload.lines() {
|
||
|
match serde_json::from_str::<ChatEvent>(line) {
|
||
|
Err(e) => timeline.push(format!("[!] failed deserializing chat message: {e} -- {payload}")).await,
|
||
|
Ok(event) => match event.inner {
|
||
|
ChatEventInner::Chat { body, visible: _ } =>
|
||
|
timeline.push(format!("{} | [{}]> {}", event.timestamp, event.user.display_name, dissolve::strip_html_tags(&body).join(" "))).await,
|
||
|
ChatEventInner::ConnectedUserInfo =>
|
||
|
timeline.push(format!("{} | {} connected", event.timestamp, event.user.display_name)).await,
|
||
|
ChatEventInner::UserJoined =>
|
||
|
timeline.push(format!("{} | {} joined", event.timestamp, event.user.display_name)).await,
|
||
|
ChatEventInner::UserParted =>
|
||
|
timeline.push(format!("{} | {} left", event.timestamp, event.user.display_name)).await,
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
|
||
|
timeline.write().await.push("[x] stream closed".to_string());
|
||
|
}
|