nederlandskie/src/streaming.rs

97 lines
2.9 KiB
Rust
Raw Normal View History

use anyhow::Result;
use crate::frames::Frame;
use anyhow::anyhow;
use atrium_api::app::bsky::feed::post::Record;
use atrium_api::com::atproto::sync::subscribe_repos::Commit;
use atrium_api::com::atproto::sync::subscribe_repos::Message;
use futures::StreamExt;
use tokio_tungstenite::{connect_async, tungstenite};
pub async fn start_stream() -> Result<()> {
let (mut stream, _) =
connect_async("wss://bsky.social/xrpc/com.atproto.sync.subscribeRepos").await?;
while let Some(Ok(tungstenite::Message::Binary(message))) = stream.next().await {
let commit = match parse_commit_message(&message) {
Ok(Some(commit)) => commit,
Ok(None) => continue,
Err(e) => {
println!("Couldn't parse commit: {:?}", e);
continue;
}
};
let post_messages = extract_post_messages(&commit).await;
match post_messages {
Ok(post_messages) => {
if !post_messages.is_empty() {
println!("{:?}", post_messages);
}
}
Err(e) => {
println!("Coudln't extract post messages: {:?}", e);
}
}
}
Ok(())
}
fn parse_commit_message(message: &[u8]) -> Result<Option<Commit>> {
match Frame::try_from(message)? {
Frame::Message(message) => match message.body {
Message::Commit(commit) => Ok(Some(*commit)),
_ => Ok(None),
},
Frame::Error(err) => panic!("Frame error: {err:?}"),
}
}
#[derive(Debug)]
enum Action {
Create,
Delete,
}
#[derive(Debug)]
struct PostMessage {
action: Action,
author_did: String,
cid: String,
uri: String,
languages: Vec<String>,
text: String,
}
async fn extract_post_messages(commit: &Commit) -> Result<Vec<PostMessage>> {
let mut posts = Vec::new();
let (items, _) = rs_car::car_read_all(&mut commit.blocks.as_slice(), true).await?;
for op in &commit.ops {
let collection = op.path.split('/').next().expect("op.path is empty");
if (op.action != "create" && op.action != "delete") || collection != "app.bsky.feed.post" {
continue;
}
if let Some((_, item)) = items.iter().find(|(cid, _)| Some(*cid) == op.cid) {
let record: Record = ciborium::from_reader(&mut item.as_slice())?;
posts.push(PostMessage {
action: if op.action == "create" {
Action::Create
} else {
Action::Delete
},
languages: record.langs.unwrap_or_else(Vec::new),
text: record.text,
author_did: commit.repo.clone(),
cid: op.cid.expect("cid is not there, what").to_string(),
uri: format!("at://{}/{}", commit.repo, op.path),
})
}
}
Ok(posts)
}