185 lines
4.7 KiB
Markdown
185 lines
4.7 KiB
Markdown
|
+++
|
||
|
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": {
|
||
|
"<name>": "<pubkey>"
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 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<String> = 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/
|