mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-19 19:46:50 +00:00
NIP-35 support
This commit is contained in:
parent
f6341fed47
commit
5653bb781a
@ -164,6 +164,34 @@ impl DbPerson {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn upsert_valid_nip05(
|
||||||
|
pubkey: PublicKeyHex,
|
||||||
|
dns_id: String,
|
||||||
|
dns_id_last_checked: u64,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let sql = "INSERT INTO person (pubkey, dns_id, dns_id_valid, dns_id_last_checked, followed) \
|
||||||
|
values (?, ?, 1, ?, 1) \
|
||||||
|
ON CONFLICT(pubkey) DO UPDATE SET dns_id=?, dns_id_valid=1, dns_id_last_checked=?, followed=1";
|
||||||
|
|
||||||
|
spawn_blocking(move || {
|
||||||
|
let maybe_db = GLOBALS.db.blocking_lock();
|
||||||
|
let db = maybe_db.as_ref().unwrap();
|
||||||
|
|
||||||
|
let mut stmt = db.prepare(sql)?;
|
||||||
|
stmt.execute((
|
||||||
|
&pubkey.0,
|
||||||
|
&dns_id,
|
||||||
|
&dns_id_last_checked,
|
||||||
|
&dns_id,
|
||||||
|
&dns_id_last_checked,
|
||||||
|
))?;
|
||||||
|
Ok::<(), Error>(())
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn delete(criteria: &str) -> Result<(), Error> {
|
pub async fn delete(criteria: &str) -> Result<(), Error> {
|
||||||
let sql = format!("DELETE FROM person WHERE {}", criteria);
|
let sql = format!("DELETE FROM person WHERE {}", criteria);
|
||||||
|
@ -213,6 +213,33 @@ impl DbPersonRelay {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn upsert_last_suggested_nip35(
|
||||||
|
person: PublicKeyHex,
|
||||||
|
relay: String,
|
||||||
|
last_suggested_nip35: u64,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let sql = "INSERT INTO person_relay (person, relay, last_suggested_nip35) \
|
||||||
|
VALUES (?, ?, ?) \
|
||||||
|
ON CONFLICT(person, relay) DO UPDATE SET last_suggested_nip35=?";
|
||||||
|
|
||||||
|
spawn_blocking(move || {
|
||||||
|
let maybe_db = GLOBALS.db.blocking_lock();
|
||||||
|
let db = maybe_db.as_ref().unwrap();
|
||||||
|
|
||||||
|
let mut stmt = db.prepare(sql)?;
|
||||||
|
stmt.execute((
|
||||||
|
&person.0,
|
||||||
|
&relay,
|
||||||
|
&last_suggested_nip35,
|
||||||
|
&last_suggested_nip35,
|
||||||
|
))?;
|
||||||
|
Ok::<(), Error>(())
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn delete(criteria: &str) -> Result<(), Error> {
|
pub async fn delete(criteria: &str) -> Result<(), Error> {
|
||||||
let sql = format!("DELETE FROM person_relay WHERE {}", criteria);
|
let sql = format!("DELETE FROM person_relay WHERE {}", criteria);
|
||||||
|
12
src/error.rs
12
src/error.rs
@ -21,6 +21,12 @@ pub enum Error {
|
|||||||
#[error("Error sending mpsc: {0}")]
|
#[error("Error sending mpsc: {0}")]
|
||||||
MpscSend(#[from] tokio::sync::mpsc::error::SendError<BusMessage>),
|
MpscSend(#[from] tokio::sync::mpsc::error::SendError<BusMessage>),
|
||||||
|
|
||||||
|
#[error("NIP-05 public key not found")]
|
||||||
|
Nip05NotFound,
|
||||||
|
|
||||||
|
#[error("NIP-35 relays not found")]
|
||||||
|
Nip35NotFound,
|
||||||
|
|
||||||
#[error("Nostr: {0}")]
|
#[error("Nostr: {0}")]
|
||||||
Nostr(#[from] nostr_types::Error),
|
Nostr(#[from] nostr_types::Error),
|
||||||
|
|
||||||
@ -30,12 +36,18 @@ pub enum Error {
|
|||||||
#[error("I/O Error: {0}")]
|
#[error("I/O Error: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("Invalid DNS ID (nip-05 / nip-35), should be user@domain")]
|
||||||
|
InvalidDnsId,
|
||||||
|
|
||||||
#[error("Invalid URI: {0}")]
|
#[error("Invalid URI: {0}")]
|
||||||
InvalidUri(#[from] http::uri::InvalidUri),
|
InvalidUri(#[from] http::uri::InvalidUri),
|
||||||
|
|
||||||
#[error("Bad integer: {0}")]
|
#[error("Bad integer: {0}")]
|
||||||
ParseInt(#[from] std::num::ParseIntError),
|
ParseInt(#[from] std::num::ParseIntError),
|
||||||
|
|
||||||
|
#[error("HTTP (reqwest) error: {0}")]
|
||||||
|
ReqwestHttpError(#[from] reqwest::Error),
|
||||||
|
|
||||||
#[error("SerdeJson Error: {0}")]
|
#[error("SerdeJson Error: {0}")]
|
||||||
SerdeJson(#[from] serde_json::Error),
|
SerdeJson(#[from] serde_json::Error),
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use crate::error::Error;
|
|||||||
use crate::globals::{Globals, GLOBALS};
|
use crate::globals::{Globals, GLOBALS};
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use minion::Minion;
|
use minion::Minion;
|
||||||
use nostr_types::{Event, PrivateKey, PublicKey, PublicKeyHex, Unixtime, Url};
|
use nostr_types::{Event, Nip05, PrivateKey, PublicKey, PublicKeyHex, Unixtime, Url};
|
||||||
use relay_picker::{BestRelay, RelayPicker};
|
use relay_picker::{BestRelay, RelayPicker};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tokio::sync::broadcast::Sender;
|
use tokio::sync::broadcast::Sender;
|
||||||
@ -349,6 +349,14 @@ impl Overlord {
|
|||||||
"get_missing_events" => {
|
"get_missing_events" => {
|
||||||
self.get_missing_events().await?;
|
self.get_missing_events().await?;
|
||||||
}
|
}
|
||||||
|
"follow_nip35" => {
|
||||||
|
let dns_id: String = serde_json::from_str(&bus_message.json_payload)?;
|
||||||
|
let _ = tokio::spawn(async move {
|
||||||
|
if let Err(e) = Overlord::get_and_follow_nip35(dns_id).await {
|
||||||
|
error!("{}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -399,4 +407,61 @@ impl Overlord {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_and_follow_nip35(nip35: String) -> Result<(), Error> {
|
||||||
|
let mut parts: Vec<&str> = nip35.split('@').collect();
|
||||||
|
if parts.len() != 2 {
|
||||||
|
return Err(Error::InvalidDnsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
let domain = parts.pop().unwrap();
|
||||||
|
let user = parts.pop().unwrap();
|
||||||
|
let nip05_future = reqwest::Client::new()
|
||||||
|
.get(format!(
|
||||||
|
"https://{}/.well-known/nostr.json?name={}",
|
||||||
|
domain, user
|
||||||
|
))
|
||||||
|
.header("Host", domain)
|
||||||
|
.send();
|
||||||
|
let timeout_future = tokio::time::timeout(std::time::Duration::new(15, 0), nip05_future);
|
||||||
|
let response = timeout_future.await??;
|
||||||
|
let nip05 = response.json::<Nip05>().await?;
|
||||||
|
Overlord::follow_nip35(nip05, user.to_string(), domain.to_string()).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn follow_nip35(nip05: Nip05, user: String, domain: String) -> Result<(), Error> {
|
||||||
|
let dns_id = format!("{}@{}", user, domain);
|
||||||
|
|
||||||
|
let pubkey = match nip05.names.get(&user) {
|
||||||
|
Some(pk) => pk,
|
||||||
|
None => return Err(Error::Nip05NotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
let relays = match nip05.relays.get(pubkey) {
|
||||||
|
Some(relays) => relays,
|
||||||
|
None => return Err(Error::Nip35NotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save person
|
||||||
|
DbPerson::upsert_valid_nip05((*pubkey).into(), dns_id, Unixtime::now().unwrap().0 as u64)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
for relay in relays.iter() {
|
||||||
|
// Save relay
|
||||||
|
let db_relay = DbRelay::new(relay.to_string())?;
|
||||||
|
DbRelay::insert(db_relay).await?;
|
||||||
|
|
||||||
|
DbPersonRelay::upsert_last_suggested_nip35(
|
||||||
|
(*pubkey).into(),
|
||||||
|
relay.0.clone(),
|
||||||
|
Unixtime::now().unwrap().0 as u64,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Followed {}@{} at {} relays", user, domain, relays.len());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,8 @@ pub fn run() -> Result<(), Error> {
|
|||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum Page {
|
enum Page {
|
||||||
Feed,
|
Feed,
|
||||||
People,
|
PeopleFollow,
|
||||||
|
PeopleList,
|
||||||
You,
|
You,
|
||||||
Relays,
|
Relays,
|
||||||
Settings,
|
Settings,
|
||||||
@ -62,6 +63,7 @@ struct GossipUi {
|
|||||||
placeholder_avatar: TextureHandle,
|
placeholder_avatar: TextureHandle,
|
||||||
draft: String,
|
draft: String,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
|
nip35follow: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GossipUi {
|
impl GossipUi {
|
||||||
@ -113,6 +115,7 @@ impl GossipUi {
|
|||||||
placeholder_avatar: placeholder_avatar_texture_handle,
|
placeholder_avatar: placeholder_avatar_texture_handle,
|
||||||
draft: "".to_owned(),
|
draft: "".to_owned(),
|
||||||
settings,
|
settings,
|
||||||
|
nip35follow: "".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,7 +135,7 @@ impl eframe::App for GossipUi {
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.selectable_value(&mut self.page, Page::Feed, "Feed");
|
ui.selectable_value(&mut self.page, Page::Feed, "Feed");
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.selectable_value(&mut self.page, Page::People, "People");
|
ui.selectable_value(&mut self.page, Page::PeopleList, "People");
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.selectable_value(&mut self.page, Page::You, "You");
|
ui.selectable_value(&mut self.page, Page::You, "You");
|
||||||
ui.separator();
|
ui.separator();
|
||||||
@ -149,7 +152,8 @@ impl eframe::App for GossipUi {
|
|||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| match self.page {
|
egui::CentralPanel::default().show(ctx, |ui| match self.page {
|
||||||
Page::Feed => feed::update(self, ctx, frame, ui),
|
Page::Feed => feed::update(self, ctx, frame, ui),
|
||||||
Page::People => people::update(self, ctx, frame, ui),
|
Page::PeopleList => people::update(self, ctx, frame, ui),
|
||||||
|
Page::PeopleFollow => people::update(self, ctx, frame, ui),
|
||||||
Page::You => you::update(self, ctx, frame, ui),
|
Page::You => you::update(self, ctx, frame, ui),
|
||||||
Page::Relays => relays::update(self, ctx, frame, ui),
|
Page::Relays => relays::update(self, ctx, frame, ui),
|
||||||
Page::Settings => settings::update(self, ctx, frame, ui, darkmode),
|
Page::Settings => settings::update(self, ctx, frame, ui, darkmode),
|
||||||
|
@ -1,9 +1,44 @@
|
|||||||
use super::GossipUi;
|
use super::{GossipUi, Page};
|
||||||
|
use crate::comms::BusMessage;
|
||||||
use crate::globals::GLOBALS;
|
use crate::globals::GLOBALS;
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use egui::{Context, RichText, ScrollArea, TextStyle, Ui, Vec2};
|
use egui::{Context, RichText, ScrollArea, TextStyle, TopBottomPanel, Ui, Vec2};
|
||||||
|
|
||||||
|
pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
|
||||||
|
TopBottomPanel::top("people_menu").show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.selectable_value(&mut app.page, Page::PeopleList, "Followed");
|
||||||
|
ui.separator();
|
||||||
|
ui.selectable_value(&mut app.page, Page::PeopleFollow, "Follow Someone New");
|
||||||
|
ui.separator();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if app.page == Page::PeopleFollow {
|
||||||
|
ui.add_space(24.0);
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.heading("Follow someone");
|
||||||
|
ui.add_space(18.0);
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Enter user@domain");
|
||||||
|
ui.text_edit_singleline(&mut app.nip35follow);
|
||||||
|
if ui.button("follow").clicked() {
|
||||||
|
let tx = GLOBALS.to_overlord.clone();
|
||||||
|
let _ = tx.send(BusMessage {
|
||||||
|
target: "overlord".to_string(),
|
||||||
|
kind: "follow_nip35".to_string(),
|
||||||
|
json_payload: serde_json::to_string(&app.nip35follow).unwrap(),
|
||||||
|
});
|
||||||
|
app.nip35follow = "".to_owned();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if app.page == Page::PeopleList {
|
||||||
|
ui.add_space(24.0);
|
||||||
|
|
||||||
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.heading("People Followed");
|
ui.heading("People Followed");
|
||||||
ui.add_space(18.0);
|
ui.add_space(18.0);
|
||||||
@ -50,3 +85,4 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user