···1-use async_trait::async_trait;
2-use rocketman::{
3- connection::JetstreamConnection,
4- handler,
5- ingestion::LexiconIngestor,
6- options::JetstreamOptions,
7- types::event::{Commit, Event},
8-};
9-use serde_json::Value;
10-use std::{collections::HashMap, sync::Arc, sync::Mutex};
11-12-#[tokio::main]
13-async fn main() {
14- // init the builder
15- let opts = JetstreamOptions::builder()
16- // your EXACT nsids
17- .wanted_collections(vec!["app.bsky.feed.post".to_string()])
18- .build();
19- // create the jetstream connector
20- let jetstream = JetstreamConnection::new(opts);
21-22- // create your ingestors
23- let mut ingestors: HashMap<String, Box<dyn LexiconIngestor + Send + Sync>> = HashMap::new();
24- ingestors.insert(
25- // your EXACT nsid
26- "app.bsky.feed.post".to_string(),
27- Box::new(MyCoolIngestor),
28- );
29-30- // tracks the last message we've processed
31- let cursor: Arc<Mutex<Option<u64>>> = Arc::new(Mutex::new(None));
32-33- // get channels
34- let msg_rx = jetstream.get_msg_rx();
35- let reconnect_tx = jetstream.get_reconnect_tx();
36-37- // spawn a task to process messages from the queue.
38- // this is a simple implementation, you can use a more complex one based on needs.
39- let c_cursor = cursor.clone();
40- tokio::spawn(async move {
41- while let Ok(message) = msg_rx.recv_async().await {
42- if let Err(e) =
43- handler::handle_message(message, &ingestors, reconnect_tx.clone(), c_cursor.clone())
44- .await
45- {
46- eprintln!("Error processing message: {}", e);
47- };
48- }
49- });
50-51- // connect to jetstream
52- // retries internally, but may fail if there is an extreme error.
53- if let Err(e) = jetstream.connect(cursor.clone()).await {
54- eprintln!("Failed to connect to Jetstream: {}", e);
55- std::process::exit(1);
56- }
57-}
58-59-pub struct MyCoolIngestor;
60-61-/// A cool ingestor implementation. Will just print the message. Does not do verification.
62-#[async_trait]
63-impl LexiconIngestor for MyCoolIngestor {
64- async fn ingest(&self, message: Event<Value>) -> anyhow::Result<()> {
65- if let Some(Commit {
66- record: Some(record),
67- ..
68- }) = message.commit
69- {
70- if let Some(Value::String(text)) = record.get("text") {
71- println!("{text:?}");
72- }
73- }
74- Ok(())
75- }
76-}
···1-## Rocketman
2-3-A modular(ish) jetstream consumer. Backed by Tungstenite.
4-5-6-### Installation
7-```toml
8-[dependencies]
9-rocketman = "latest" # pyt the latest version here
10-tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
11-```
12-### Usage
13-```rs
14-#[tokio::main]
15-async fn main() {
16- // init the builder
17- let opts = JetstreamOptions::builder()
18- // your EXACT nsids
19- .wanted_collections(vec!["com.example.cool.nsid".to_string()])
20- .build();
21- // create the jetstream connector
22- let jetstream = JetstreamConnection::new(opts);
23-24- // create your ingestors
25- let mut ingestors: HashMap<String, Box<dyn LexiconIngestor + Send + Sync>> = HashMap::new();
26- ingestors.insert(
27- // your EXACT nsid
28- "com.example.cool.nsid".to_string(),
29- Box::new(MyCoolIngestor),
30- );
31-32-33- // tracks the last message we've processed
34- let cursor: Arc<Mutex<Option<u64>>> = Arc::new(Mutex::new(None));
35-36- // get channels
37- let msg_rx = jetstream.get_msg_rx();
38- let reconnect_tx = jetstream.get_reconnect_tx();
39-40- // spawn a task to process messages from the queue.
41- // this is a simple implementation, you can use a more complex one based on needs.
42- let c_cursor = cursor.clone();
43- tokio::spawn(async move {
44- while let Ok(message) = msg_rx.recv_async().await {
45- if let Err(e) =
46- handler::handle_message(message, &ingestors, reconnect_tx.clone(), c_cursor.clone())
47- .await
48- {
49- error!("Error processing message: {}", e);
50- };
51- }
52- });
53-54- // connect to jetstream
55- // retries internally, but may fail if there is an extreme error.
56- if let Err(e) = jetstream.connect(cursor.clone()).await {
57- error!("Failed to connect to Jetstream: {}", e);
58- std::process::exit(1);
59- }
60-}
61-62-pub struct MyCoolIngestor;
63-64-/// A cool ingestor implementation. Will just print the message. Does not do verification.
65-impl LexiconIngestor for MyCoolIngestor {
66- async fn ingest(&self, message: Event<Value>) -> Result<()> {
67- info!("{:?}", message);
68- // Process message for default lexicon.
69- Ok(())
70- }
71-}
72-```
73-### gratz
74-Based heavily on [phil's jetstream consumer on atcosm constellation.](https://github.com/atcosm/links/blob/main/constellation/src/consumer/jetstream.rs)