+++ date = "2023-02-05" tags = ["nostr", "rust", "damus","sns"] title = "damusをはじめてみた" slug = "damus" +++ damusはnostr protocolに対応したiosのclientです。 - https://github.com/nostr-protocol/nostr - https://github.com/damus-io/damus - https://www.reddit.com/r/nostr/comments/10qmsos/damus_for_ios_is_live/ ### verify https://gist.github.com/metasikander/609a538e6a03b2f67e5c8de625baed3e public-keyはhexに変換します。 https://damus.io/key/ ```sh # domain root $ cd public $ vim ./.well-known/nostr.json ``` ```json:/.well-known/nostr.json { "names": { "": "" } } ``` ### web client https://snort.social ### cli client とりあえずcli client作ってpostしてみた。 ![](https://raw.githubusercontent.com/syui/img/master/other/nostr_client_rust_20230205_0001.jpg) exampleとしてはこんな感じになるんじゃないかというのを色々なパターンで書いてみた。 ```rust:src/main.rs pub mod data; use std::env; use url::Url; use data::Data as Datas; use seahorse::{App, Command, Context}; use nostr::{Event, EventBuilder, Metadata}; use nostr::key::{FromBech32, Keys}; use nostr::message::ClientMessage; use tungstenite::{connect, Message as WsMessage}; const WS_ENDPOINT: &str = "wss://relay.damus.io"; use nostr_sdk::{Client}; use nostr::SubscriptionFilter; use nostr::util::time::timestamp; fn main() { let args: Vec = env::args().collect(); let app = App::new(env!("CARGO_PKG_NAME")) .author(env!("CARGO_PKG_AUTHORS")) .description(env!("CARGO_PKG_DESCRIPTION")) .version(env!("CARGO_PKG_VERSION")) .usage("nor [option] [x]") .command( Command::new("timeline") .usage("nor t") .description("timeline") .alias("t") .action(t), ) .command( Command::new("notify") .usage("nor n") .description("notify") .alias("n") .action(n), ) .command( Command::new("status") .usage("nor s") .description("status") .alias("s") .action(s), ) .command( Command::new("post") .usage("nor p {}") .description("post message, ex: $ nor p $text") .alias("p") .action(p), ) ; app.run(args); } #[tokio::main] async fn timeline() -> anyhow::Result<()> { let data = Datas::new().unwrap(); let my_keys = Keys::from_bech32(&data.secret_key)?; let mut client = Client::new(&my_keys); client .add_relay("wss://relay.damus.io", None) .unwrap(); client.connect_relay("wss://relay.damus.io", true).await.unwrap(); client.connect().await?; let subscription = SubscriptionFilter::new() .pubkeys(vec![my_keys.public_key()]) .since(timestamp()); let t = client.get_events_of(vec![subscription]).await.unwrap(); println!("{:#?}", t); Ok(()) } fn t(_c: &Context) { timeline().unwrap(); } #[tokio::main] async fn notify() -> anyhow::Result<()> { let data = Datas::new().unwrap(); let my_keys = Keys::from_bech32(&data.secret_key)?; let client = Client::new(&my_keys); let notifications = client.notifications(); println!("{:?}", notifications); Ok(()) //loop { // let mut notifications = client.notifications(); // while let Ok(notification) = notifications.recv().await { // println!("{:?}", notification); // } //} } fn n(_c: &Context) { notify().unwrap(); } fn status() -> anyhow::Result<()> { let data = Datas::new().unwrap(); let metadata = Metadata::new() .name(data.name) .display_name(data.display_name) .about(data.about) .picture(Url::parse(&data.picture)?) .nip05(data.nip); let my_keys = Keys::from_bech32(&data.secret_key)?; let event: Event = EventBuilder::set_metadata(&my_keys, metadata)?.to_event(&my_keys)?; println!("{:#?}", event); Ok(()) } fn s(_c: &Context) { status().unwrap(); } fn post(c: &Context) -> anyhow::Result<()> { let text = c.args[0].to_string(); let data = Datas::new().unwrap(); let my_keys = Keys::from_bech32(&data.secret_key)?; let event: Event = EventBuilder::new_text_note(&text, &[]).to_event(&my_keys)?; let (mut socket, _response) = connect(WS_ENDPOINT).expect("Can't connect to relay"); let msg = ClientMessage::new_event(event).to_json(); socket.write_message(WsMessage::Text(msg))?; Ok(()) } fn p(c: &Context) { post(c).unwrap(); } ``` - https://docs.rs/nostr/latest/nostr/ - https://docs.rs/nostr-sdk/latest/nostr_sdk/