Fix relay selection

This commit is contained in:
Jon Staab
2026-03-26 12:28:49 -07:00
parent c4a63b18af
commit 05437ef113
3 changed files with 42 additions and 48 deletions
+4 -5
View File
@@ -8,15 +8,14 @@ Members:
- `name: String` - the name of the bot, from `ROBOT_NAME` - `name: String` - the name of the bot, from `ROBOT_NAME`
- `description: String` - the description of the bot, from `ROBOT_DESCRIPTION` - `description: String` - the description of the bot, from `ROBOT_DESCRIPTION`
- `picture: String` - the picture URL for the bot, from `ROBOT_PICTURE` - `picture: String` - the picture URL for the bot, from `ROBOT_PICTURE`
- `outbox_relays: Vec<String>` - outbox relay URLs, from `ROBOT_OUTBOX_RELAYS` - `outbox_client: nostr_sdk::Client` - used for publishing relay lists and metadata, connects to `ROBOT_OUTBOX_RELAYS`
- `indexer_relays: Vec<String>` - indexer relay URLs, from `ROBOT_INDEXER_RELAYS` - `indexer_client: nostr_sdk::Client` - used for publishing relay lists, connects to `ROBOT_INDEXER_RELAYS`
- `messaging_relays: Vec<String>` - messaging relay URLs, from `ROBOT_MESSAGING_RELAYS` - `messagins_client: nostr_sdk::Client` - used for sending and receiving dms, connects to `ROBOT_MESSAGING_RELAYS`
- `client: nostr_sdk::Client`
## `pub fn new() -> Self` ## `pub fn new() -> Self`
- Reads environment and populates members. Relay urls should be split and normalized. - Reads environment and populates members. Relay urls should be split and normalized.
- Publishes a `kind 0` nostr profile, a `kind 10002` relay list, and `kind 10050` relay selections using `client` - Publishes a `kind 0` nostr profile, a `kind 10002` relay list, and `kind 10050` relay selections
## `pub async fn send_dm(&self, recipient: &str, message: &str) -> Result<()>` ## `pub async fn send_dm(&self, recipient: &str, message: &str) -> Result<()>`
+4 -4
View File
@@ -16,9 +16,8 @@ pub struct Repo {
impl Repo { impl Repo {
pub async fn new() -> Result<Self> { pub async fn new() -> Result<Self> {
let raw_database_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| { let raw_database_url = std::env::var("DATABASE_URL")
format!("sqlite://{}/data/caravel.db", env!("CARGO_MANIFEST_DIR")) .unwrap_or_else(|_| format!("sqlite://{}/data/caravel.db", env!("CARGO_MANIFEST_DIR")));
});
let database_url = normalize_sqlite_url(&raw_database_url); let database_url = normalize_sqlite_url(&raw_database_url);
if let Some(path) = database_url.strip_prefix("sqlite://") if let Some(path) = database_url.strip_prefix("sqlite://")
@@ -30,7 +29,8 @@ impl Repo {
std::fs::create_dir_all(parent)?; std::fs::create_dir_all(parent)?;
} }
let connect_options = SqliteConnectOptions::from_str(&database_url)?.create_if_missing(true); let connect_options =
SqliteConnectOptions::from_str(&database_url)?.create_if_missing(true);
let pool = SqlitePoolOptions::new() let pool = SqlitePoolOptions::new()
.max_connections(5) .max_connections(5)
+34 -39
View File
@@ -11,10 +11,9 @@ pub struct Robot {
name: String, name: String,
description: String, description: String,
picture: String, picture: String,
outbox_relays: Vec<String>, outbox_client: Client,
indexer_relays: Vec<String>, indexer_client: Client,
messaging_relays: Vec<String>, messaging_client: Client,
client: Client,
outbox_cache: std::sync::Arc<Mutex<HashMap<String, CacheEntry>>>, outbox_cache: std::sync::Arc<Mutex<HashMap<String, CacheEntry>>>,
dm_cache: std::sync::Arc<Mutex<HashMap<String, CacheEntry>>>, dm_cache: std::sync::Arc<Mutex<HashMap<String, CacheEntry>>>,
} }
@@ -49,37 +48,33 @@ impl Robot {
return Err(anyhow!("ROBOT_MESSAGING_RELAYS is required")); return Err(anyhow!("ROBOT_MESSAGING_RELAYS is required"));
} }
let keys = Keys::parse(&secret)?; let outbox_client = client_with_relays(&secret, &outbox_relays).await?;
let client = Client::new(keys); let indexer_client = client_with_relays(&secret, &indexer_relays).await?;
for relay in &outbox_relays { let messaging_client = client_with_relays(&secret, &messaging_relays).await?;
client.add_relay(relay).await?;
}
for relay in &indexer_relays {
client.add_relay(relay).await?;
}
for relay in &messaging_relays {
client.add_relay(relay).await?;
}
client.connect().await;
let robot = Self { let robot = Self {
secret, secret,
name, name,
description, description,
picture, picture,
outbox_relays, outbox_client,
indexer_relays, indexer_client,
messaging_relays, messaging_client,
client,
outbox_cache: std::sync::Arc::new(Mutex::new(HashMap::new())), outbox_cache: std::sync::Arc::new(Mutex::new(HashMap::new())),
dm_cache: std::sync::Arc::new(Mutex::new(HashMap::new())), dm_cache: std::sync::Arc::new(Mutex::new(HashMap::new())),
}; };
robot.publish_identity().await?; robot
.publish_identity(&outbox_relays, &messaging_relays)
.await?;
Ok(robot) Ok(robot)
} }
async fn publish_identity(&self) -> Result<()> { async fn publish_identity(
&self,
outbox_relays: &[String],
messaging_relays: &[String],
) -> Result<()> {
let mut metadata = Metadata::new(); let mut metadata = Metadata::new();
if !self.name.is_empty() { if !self.name.is_empty() {
metadata = metadata.name(&self.name); metadata = metadata.name(&self.name);
@@ -91,25 +86,24 @@ impl Robot {
metadata = metadata.picture(Url::parse(&self.picture)?); metadata = metadata.picture(Url::parse(&self.picture)?);
} }
self.client self.outbox_client
.send_event_builder(EventBuilder::metadata(&metadata)) .send_event_builder(EventBuilder::metadata(&metadata))
.await?; .await?;
let outbox_tags = self let outbox_tags = outbox_relays
.outbox_relays
.iter() .iter()
.map(|r| Tag::parse(["r", r.as_str()])) .map(|r| Tag::parse(["r", r.as_str()]))
.collect::<std::result::Result<Vec<_>, _>>()?; .collect::<std::result::Result<Vec<_>, _>>()?;
self.client self.outbox_client
.send_event_builder(EventBuilder::new(Kind::Custom(10002), "").tags(outbox_tags)) .send_event_builder(EventBuilder::new(Kind::Custom(10002), "").tags(outbox_tags))
.await?; .await?;
let mut selection_tags = Vec::new(); let messaging_tags = messaging_relays
for relay in &self.messaging_relays { .iter()
selection_tags.push(Tag::parse(["relay", relay.as_str()])?); .map(|r| Tag::parse(["relay", r.as_str()]))
} .collect::<std::result::Result<Vec<_>, _>>()?;
self.client self.indexer_client
.send_event_builder(EventBuilder::new(Kind::Custom(10050), "").tags(selection_tags)) .send_event_builder(EventBuilder::new(Kind::Custom(10050), "").tags(messaging_tags))
.await?; .await?;
Ok(()) Ok(())
@@ -129,10 +123,9 @@ impl Robot {
} }
let recipient_pubkey = PublicKey::parse(recipient)?; let recipient_pubkey = PublicKey::parse(recipient)?;
let keys = Keys::parse(&self.secret)?; let client = self.messaging_client.clone();
let client = Client::new(keys);
for relay in dm_relays { for relay in dm_relays {
client.add_relay(relay).await?; let _ = client.add_relay(relay).await;
} }
client.connect().await; client.connect().await;
client client
@@ -147,9 +140,11 @@ impl Robot {
} }
let pubkey = PublicKey::parse(recipient)?; let pubkey = PublicKey::parse(recipient)?;
let client = indexer_client(&self.secret, &self.indexer_relays).await?;
let filter = Filter::new().author(pubkey).kind(Kind::Custom(10002)); let filter = Filter::new().author(pubkey).kind(Kind::Custom(10002));
let events = client.fetch_events(filter, Duration::from_secs(5)).await?; let events = self
.indexer_client
.fetch_events(filter, Duration::from_secs(5))
.await?;
let mut relays = Vec::new(); let mut relays = Vec::new();
if let Some(event) = events.into_iter().max_by_key(|e| e.created_at) { if let Some(event) = events.into_iter().max_by_key(|e| e.created_at) {
@@ -221,10 +216,10 @@ fn normalize_relay_url(url: &str) -> String {
} }
} }
async fn indexer_client(secret: &str, indexer_relays: &[String]) -> Result<Client> { async fn client_with_relays(secret: &str, relays: &[String]) -> Result<Client> {
let keys = Keys::parse(secret)?; let keys = Keys::parse(secret)?;
let client = Client::new(keys); let client = Client::new(keys);
for relay in indexer_relays { for relay in relays {
client.add_relay(relay).await?; client.add_relay(relay).await?;
} }
client.connect().await; client.connect().await;