1 Commits

Author SHA1 Message Date
9845dd025d added metadata and test 2026-01-31 16:27:12 +01:00
6 changed files with 178 additions and 1 deletions

View File

@@ -13,6 +13,7 @@
* **Social Connectivity**: Follow users and manage contact lists.
* **Rich Feeds**: Access follow-based timelines, global discovery, and hashtag-based searches.
* **Encrypted Messaging**: Secure NIP-17 direct messages.
* **Profile Management**: Update account metadata (Kind 0) with support for standard and custom fields.
* **Extensible Architecture**: Clean separation between protocol logic (NIPs) and high-level functions.
---
@@ -67,6 +68,24 @@ let event_id = en.post_text("Building with EasyNostr is awesome!").await?;
println!("Successfully posted! Event ID: {}", event_id);
```
### 👤 Profile & Metadata
Update account information (NIP-01) including support for custom fields (e.g., bot, birthday).
```rust
en.set_metadata(
Some("user_name".to_string()),
Some("Display Name".to_string()),
Some("About me...".to_string()),
Some("user@domain.com".to_string()), // NIP-05
Some("user@getalby.com".to_string()), // LUD-16
Some("https://website.com".to_string()),
Some("https://banner.com/img.png".to_string()),
Some(false), // bot (custom field)
Some("1990-01-01".to_string()), // birthday (custom field)
).await?;
```
### 📰 Social Feeds
#### Timeline (Followed Users)

View File

@@ -112,6 +112,26 @@ async fn main() -> anyhow::Result<()> {
println!(" [WARN] No posts found for tags (depends on relay traffic).");
}
// 10. Metadata Update (Kind 0)
println!("\n[STEP 8] Testing Profile Metadata Update...");
let metadata_id = ez
.set_metadata(
Some("EasyNostr Test Bot".to_string()),
Some("EasyNostr".to_string()),
Some("Automated test for metadata updates".to_string()),
None, // NIP-05
None, // LUD-16
Some("https://github.com/Bytemalte/easy-nostr".to_string()),
Some(
"https://raw.githubusercontent.com/Bytemalte/easy-nostr/main/banner.png"
.to_string(),
),
Some(true), // bot
Some("2024-01-31".to_string()), // birthday
)
.await?;
println!(" [OK] Metadata updated: {}", metadata_id.to_bech32()?);
println!("\n=== Test Complete ===");
Ok(())
}

64
src/functions/metadata.rs Normal file
View File

@@ -0,0 +1,64 @@
use crate::nips::nip01;
use anyhow::{Result, bail};
use nostr_sdk::prelude::*;
/// Updates the metadata of the current account.
///
/// # Arguments
/// * `client` - The Nostr client.
/// * `name` - Optional name.
/// * `display_name` - Optional display name.
/// * `about` - Optional about description.
/// * `nip05` - Optional NIP-05 identifier.
/// * `lud16` - Optional LDP-16 (Lightning Address).
/// * `website` - Optional website URL.
/// * `banner` - Optional banner image URL.
/// * `bot` - Optional boolean identifying if the account is a bot.
/// * `birthday` - Optional birthday string.
pub async fn set_metadata(
client: &Client,
name: Option<String>,
display_name: Option<String>,
about: Option<String>,
nip05: Option<String>,
lud16: Option<String>,
website: Option<String>,
banner: Option<String>,
bot: Option<bool>,
birthday: Option<String>,
) -> Result<EventId> {
// Basic validation for NIP-05 and LUD-16
if let Some(ref n05) = nip05 {
if !n05.contains('@') {
bail!("Invalid NIP-05 format: must contain '@'");
}
}
if let Some(ref l16) = lud16 {
if !l16.contains('@') {
bail!("Invalid LUD-16 format: must contain '@'");
}
}
// Parse URLs if provided
let website_url = website.map(|w| Url::parse(&w)).transpose()?;
let banner_url = banner.map(|b| Url::parse(&b)).transpose()?;
// Delegate creation of metadata struct to nip01
let metadata = nip01::create_metadata(
name,
display_name,
about,
nip05,
lud16,
website_url,
banner_url,
bot,
birthday,
);
// Send the metadata event (Kind 0)
let event_id = client.set_metadata(&metadata).await?;
Ok(*event_id)
}

View File

@@ -3,6 +3,7 @@ pub mod feed;
pub mod get_contacts;
pub mod hashtags;
pub mod messages;
pub mod metadata;
pub mod new;
pub mod publish;
pub mod relays;

View File

@@ -6,7 +6,7 @@ use nostr_sdk::prelude::*;
// Wir importieren die Funktionen, um Tipparbeit zu sparen
use crate::functions::{
create_contact, feed, get_contacts, hashtags, messages, new, publish, relays,
create_contact, feed, get_contacts, hashtags, messages, metadata, new, publish, relays,
};
use crate::nips::nip01::Post;
use crate::nips::nip17::Message;
@@ -70,4 +70,32 @@ impl EasyNostr {
pub async fn get_posts_by_hashtags(&self, hashtags: Vec<String>) -> Result<Vec<Post>> {
hashtags::get_posts_by_hashtags(&self.client, hashtags).await
}
/// Aktualisiert das Profil-Metadaten (Kind 0)
pub async fn set_metadata(
&self,
name: Option<String>,
display_name: Option<String>,
about: Option<String>,
nip05: Option<String>,
lud16: Option<String>,
website: Option<String>,
banner: Option<String>,
bot: Option<bool>,
birthday: Option<String>,
) -> Result<EventId> {
metadata::set_metadata(
&self.client,
name,
display_name,
about,
nip05,
lud16,
website,
banner,
bot,
birthday,
)
.await
}
}

View File

@@ -17,3 +17,48 @@ pub async fn publish_text(client: &Client, content: &str) -> Result<EventId> {
let output = client.send_event_builder(builder).await?;
Ok(*output.id())
}
/// Creates a Metadata object for updating profile (Kind 0)
pub fn create_metadata(
name: Option<String>,
display_name: Option<String>,
about: Option<String>,
nip05: Option<String>,
lud16: Option<String>,
website: Option<Url>,
banner: Option<Url>,
bot: Option<bool>,
birthday: Option<String>,
) -> Metadata {
let mut metadata = Metadata::new();
if let Some(val) = name {
metadata = metadata.name(val);
}
if let Some(val) = display_name {
metadata = metadata.display_name(val);
}
if let Some(val) = about {
metadata = metadata.about(val);
}
if let Some(val) = nip05 {
metadata = metadata.nip05(val);
}
if let Some(val) = lud16 {
metadata = metadata.lud16(val);
}
if let Some(val) = website {
metadata = metadata.website(val);
}
if let Some(val) = banner {
metadata = metadata.banner(val);
}
if let Some(val) = bot {
metadata = metadata.custom_field("bot", val);
}
if let Some(val) = birthday {
metadata = metadata.custom_field("birthday", val);
}
metadata
}