use async_trait::async_trait;
use rocketman::{
connection::JetstreamConnection,
handler::{self, Ingestors},
ingestion::LexiconIngestor,
options::JetstreamOptions,
types::event::{Account, Commit, Event, Identity},
};
use serde_json::Value;
use std::{sync::Arc, sync::Mutex};
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.init();
let opts = JetstreamOptions::builder()
.wanted_collections(vec![
"app.bsky.feed.post".to_string(),
"xyz.statusphere.status".to_string(),
])
.build();
let jetstream = JetstreamConnection::new(opts);
let mut ingestors = Ingestors::new();
ingestors.commits.insert(
"xyz.statusphere.status".to_string(),
Box::new(StatusphereIngestor),
);
ingestors.identity = Some(Box::new(IdentityIngestor));
ingestors.account = Some(Box::new(AccountIngestor));
let cursor: Arc<Mutex<Option<u64>>> = Arc::new(Mutex::new(None));
let msg_rx = jetstream.get_msg_rx();
let reconnect_tx = jetstream.get_reconnect_tx();
let c_cursor = cursor.clone();
tokio::spawn(async move {
while let Ok(message) = msg_rx.recv().await {
if let Err(e) =
handler::handle_message(message, &ingestors, reconnect_tx.clone(), c_cursor.clone())
.await
{
eprintln!("Error processing message: {}", e);
};
}
});
if let Err(e) = jetstream.connect(cursor.clone()).await {
eprintln!("Failed to connect to Jetstream: {}", e);
std::process::exit(1);
}
}
pub struct StatusphereIngestor;
#[async_trait]
impl LexiconIngestor for StatusphereIngestor {
async fn ingest(&self, message: Event<Value>) -> anyhow::Result<()> {
if let Some(Commit {
record: Some(record),
operation,
..
}) = message.commit
{
if let Some(Value::String(status)) = record.get("status") {
println!("[STATUSPHERE] [{operation:?}] {status:?}");
}
}
Ok(())
}
}
pub struct IdentityIngestor;
#[async_trait]
impl LexiconIngestor for IdentityIngestor {
async fn ingest(&self, message: Event<Value>) -> anyhow::Result<()> {
if let Some(Identity {
did,
handle,
seq,
time,
}) = message.identity
{
println!("[IDENTITY] seq={seq} did={did} handle={handle:?} time={time}");
}
Ok(())
}
}
pub struct AccountIngestor;
#[async_trait]
impl LexiconIngestor for AccountIngestor {
async fn ingest(&self, message: Event<Value>) -> anyhow::Result<()> {
if let Some(Account {
did,
handle,
seq,
time,
status,
}) = message.account
{
let handle_str = handle
.as_ref()
.map(|h| format!(" handle={h}"))
.unwrap_or_default();
let status_str = status
.as_ref()
.map(|s| format!(" status={s:?}"))
.unwrap_or_default();
println!("[ACCOUNT] seq={seq} did={did}{handle_str}{status_str} time={time}");
}
Ok(())
}
}