|
@@ -1,11 +1,25 @@
|
|
|
+use std::{
|
|
|
+ collections::{HashMap, HashSet},
|
|
|
+ ops::Deref,
|
|
|
+};
|
|
|
+
|
|
|
use futures::future::join_all;
|
|
|
use nostr_rs_client::Pool;
|
|
|
use nostr_rs_relayer::Relayer;
|
|
|
use nostr_rs_storage_base::Storage;
|
|
|
-use nostr_rs_types::types::{Addr, Filter, Id};
|
|
|
+use nostr_rs_types::{
|
|
|
+ types::{content::profile::Profile, Content, Event, Filter, Id, Kind, Tag},
|
|
|
+ Request,
|
|
|
+};
|
|
|
+use serde::Serialize;
|
|
|
use tokio::{net::TcpListener, task::JoinHandle};
|
|
|
use url::Url;
|
|
|
|
|
|
+//
|
|
|
+// g
|
|
|
+use tokio::fs::OpenOptions;
|
|
|
+use tokio::io::AsyncWriteExt;
|
|
|
+
|
|
|
pub struct Stoppable(Option<Vec<JoinHandle<()>>>);
|
|
|
|
|
|
impl From<Vec<JoinHandle<()>>> for Stoppable {
|
|
@@ -24,6 +38,14 @@ impl Drop for Stoppable {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+impl Stoppable {
|
|
|
+ pub async fn wait(mut self) {
|
|
|
+ if let Some(tasks) = self.0.take() {
|
|
|
+ join_all(tasks).await;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
pub enum Error {
|
|
|
#[error("Relayer: {0}")]
|
|
@@ -33,42 +55,158 @@ pub enum Error {
|
|
|
Client(#[from] nostr_rs_client::Error),
|
|
|
}
|
|
|
|
|
|
+#[derive(Debug, Clone, Serialize)]
|
|
|
+pub struct Contact {
|
|
|
+ pub pub_key: Id,
|
|
|
+ pub added_by: Option<Id>,
|
|
|
+ pub profile: Option<Profile>,
|
|
|
+ pub followed_by: HashSet<Id>,
|
|
|
+ pub following: HashSet<Id>,
|
|
|
+ pub content: Vec<Event>,
|
|
|
+}
|
|
|
+
|
|
|
+impl Contact {
|
|
|
+ pub fn new(pub_key: Id, added_by: Option<Id>) -> Self {
|
|
|
+ Self {
|
|
|
+ pub_key,
|
|
|
+ profile: None,
|
|
|
+ added_by,
|
|
|
+ followed_by: HashSet::new(),
|
|
|
+ following: HashSet::new(),
|
|
|
+ content: Vec::new(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
pub struct PersonalRelayer<T: Storage + Send + Sync + 'static> {
|
|
|
relayer: Relayer<T>,
|
|
|
- accounts: Vec<Id>,
|
|
|
+ accounts: HashMap<Id, Contact>,
|
|
|
}
|
|
|
|
|
|
impl<T: Storage + Send + Sync + 'static> PersonalRelayer<T> {
|
|
|
pub async fn new(storage: T, accounts: Vec<Id>, client_urls: Vec<Url>) -> Result<Self, Error> {
|
|
|
- let pool = Pool::new_with_clients(client_urls);
|
|
|
-
|
|
|
- join_all(
|
|
|
- accounts
|
|
|
- .iter()
|
|
|
- .map(|account| {
|
|
|
- pool.subscribe(
|
|
|
- Filter {
|
|
|
- authors: vec![account.clone()],
|
|
|
- ..Default::default()
|
|
|
- }
|
|
|
- .into(),
|
|
|
- )
|
|
|
- })
|
|
|
- .collect::<Vec<_>>(),
|
|
|
- )
|
|
|
- .await
|
|
|
- .into_iter()
|
|
|
- .collect::<Result<Vec<_>, _>>()?;
|
|
|
+ let (pool, _active_clients) = Pool::new_with_clients(client_urls)?;
|
|
|
+ let relayer = Relayer::new(Some(storage), Some(pool))?;
|
|
|
|
|
|
Ok(Self {
|
|
|
- relayer: Relayer::new(Some(storage), Some(pool))?,
|
|
|
- accounts,
|
|
|
+ relayer,
|
|
|
+ accounts: accounts
|
|
|
+ .into_iter()
|
|
|
+ .map(|a| (a.clone(), Contact::new(a, None)))
|
|
|
+ .collect::<HashMap<_, _>>(),
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- pub fn main(self, server: TcpListener) -> Result<Stoppable, Error> {
|
|
|
- let (relayer, handle) = self.relayer.main(server)?;
|
|
|
- let tasks = vec![handle, tokio::spawn(async move {})];
|
|
|
+ pub async fn main(mut self, server: TcpListener) -> Result<Stoppable, Error> {
|
|
|
+ let (relayer, relayer_handler) = self.relayer.main(server)?;
|
|
|
+
|
|
|
+ let tasks = vec![
|
|
|
+ relayer_handler,
|
|
|
+ tokio::spawn(async move {
|
|
|
+ let mut local_connection = relayer.create_new_local_connection().await;
|
|
|
+ local_connection
|
|
|
+ .send(Request::Request(
|
|
|
+ vec![Filter {
|
|
|
+ authors: self.accounts.keys().map(|x| x.clone()).collect::<Vec<_>>(),
|
|
|
+ //kinds: vec![Kind::Metadata, Kind::ShortTextNote, Kind::Contacts],
|
|
|
+ ..Default::default()
|
|
|
+ }]
|
|
|
+ .into(),
|
|
|
+ ))
|
|
|
+ .await
|
|
|
+ .expect("Failed to send request");
|
|
|
+
|
|
|
+ loop {
|
|
|
+ while let Some(res) = local_connection.recv().await {
|
|
|
+ if let Some(event) = res.as_event() {
|
|
|
+ match event.content() {
|
|
|
+ Content::Metadata(profile) => {
|
|
|
+ self.accounts.get_mut(&event.author()).map(|account| {
|
|
|
+ account.profile = Some(profile.clone());
|
|
|
+ account.content.push(event.deref().clone());
|
|
|
+ });
|
|
|
+ }
|
|
|
+ Content::Contacts(_) => {
|
|
|
+ let current = event.author().clone();
|
|
|
+ let mut current_user = if let Some(current_user) =
|
|
|
+ self.accounts.remove(¤t)
|
|
|
+ {
|
|
|
+ current_user
|
|
|
+ } else {
|
|
|
+ continue;
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut ids = vec![];
|
|
|
+
|
|
|
+ for tag in event.tags() {
|
|
|
+ match tag {
|
|
|
+ Tag::PubKey(pub_key, relayer_url, _) => {
|
|
|
+ let followed = self
|
|
|
+ .accounts
|
|
|
+ .entry(pub_key.clone())
|
|
|
+ .or_insert(Contact::new(
|
|
|
+ pub_key.clone(),
|
|
|
+ Some(current.clone()),
|
|
|
+ ));
|
|
|
+
|
|
|
+ if let Some((Some(relayer_url), _)) = relayer_url {
|
|
|
+ let _ = relayer
|
|
|
+ .connect_to_relayer(relayer_url.clone())
|
|
|
+ .await;
|
|
|
+ }
|
|
|
+
|
|
|
+ current_user.following.insert(pub_key.clone());
|
|
|
+ followed.followed_by.insert(current.clone());
|
|
|
+ ids.push(pub_key.clone());
|
|
|
+ }
|
|
|
+ _ => {}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ self.accounts.insert(current, current_user);
|
|
|
+
|
|
|
+ let _ = local_connection
|
|
|
+ .send(Request::Request(
|
|
|
+ Filter {
|
|
|
+ authors: ids,
|
|
|
+ kinds: vec![
|
|
|
+ Kind::Metadata,
|
|
|
+ Kind::ShortTextNote,
|
|
|
+ Kind::Contacts,
|
|
|
+ ],
|
|
|
+ ..Default::default()
|
|
|
+ }
|
|
|
+ .into(),
|
|
|
+ ))
|
|
|
+ .await;
|
|
|
+ }
|
|
|
+ Content::ShortTextNote(_) => {
|
|
|
+ self.accounts.get_mut(&event.author()).map(|account| {
|
|
|
+ account.content.push(event.deref().clone());
|
|
|
+ });
|
|
|
+ }
|
|
|
+ _ => {}
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ println!("Not an event: {:?}", res);
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut file = OpenOptions::new()
|
|
|
+ .write(true)
|
|
|
+ .create(true)
|
|
|
+ .open("example.txt")
|
|
|
+ .await
|
|
|
+ .expect("Failed to open file");
|
|
|
+
|
|
|
+ file.write_all(&serde_json::to_vec_pretty(&self.accounts).unwrap())
|
|
|
+ .await
|
|
|
+ .expect("Failed to write to file");
|
|
|
+
|
|
|
+ file.flush().await.expect("Failed to flush file");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ ];
|
|
|
Ok(tasks.into())
|
|
|
}
|
|
|
}
|