use nostr::nips::nip44;
use nostr_sdk::prelude::*;
use crate::error::{MostroError, ServiceError};
const RANGE_RANDOM_TIMESTAMP_TWEAK_SECS: u64 = 172_800;
pub async fn wrap_chat_message(
sender_trade_keys: &Keys,
conv: &Keys,
sign: &Keys,
message: &str,
) -> Result<Event, MostroError> {
wrap_chat_message_with_tags(sender_trade_keys, conv, sign, message, Vec::new()).await
}
pub async fn wrap_chat_message_with_tags(
sender_trade_keys: &Keys,
conv: &Keys,
sign: &Keys,
message: &str,
extra_tags: Vec<Tag>,
) -> Result<Event, MostroError> {
if extra_tags.iter().any(|t| t.kind() == "p") {
return Err(MostroError::MostroInternalErr(
ServiceError::UnexpectedError("extra_tags must not contain a p tag".to_string()),
));
}
let now = Timestamp::now();
let inner = EventBuilder::new(Kind::TextNote, message)
.custom_created_at(now)
.finalize_unsigned(sender_trade_keys.public_key())
.finalize_async(sender_trade_keys)
.await
.map_err(|e| MostroError::MostroInternalErr(ServiceError::NostrError(e.to_string())))?;
let content = nip44::encrypt(
conv.secret_key(),
&conv.public_key(),
inner.as_json(),
nip44::Version::V2,
)
.map_err(|e| MostroError::MostroInternalErr(ServiceError::EncryptionError(e.to_string())))?;
let mut tags = vec![Tag::public_key(conv.public_key())];
tags.extend(extra_tags);
EventBuilder::new(Kind::PrivateDirectMessage, content)
.tags(tags)
.custom_created_at(now)
.finalize(sign)
.map_err(|e| MostroError::MostroInternalErr(ServiceError::NostrError(e.to_string())))
}
pub async fn wrap_giftwrap_chat_message(
sender_trade_keys: &Keys,
shared_pubkey: &PublicKey,
message: &str,
) -> Result<Event, MostroError> {
let inner = EventBuilder::new(Kind::TextNote, message)
.finalize_unsigned(sender_trade_keys.public_key())
.finalize_async(sender_trade_keys)
.await
.map_err(|e| MostroError::MostroInternalErr(ServiceError::NostrError(e.to_string())))?;
let ephemeral = Keys::generate();
let encrypted = nip44::encrypt(
ephemeral.secret_key(),
shared_pubkey,
inner.as_json(),
nip44::Version::V2,
)
.map_err(|e| MostroError::MostroInternalErr(ServiceError::EncryptionError(e.to_string())))?;
EventBuilder::new(Kind::GiftWrap, encrypted)
.tag(Tag::public_key(*shared_pubkey))
.custom_created_at(tweaked_timestamp())
.finalize(&ephemeral)
.map_err(|e| MostroError::MostroInternalErr(ServiceError::NostrError(e.to_string())))
}
fn tweaked_timestamp() -> Timestamp {
let now = Timestamp::now().as_secs();
let entropy = Keys::generate();
let bytes = entropy.secret_key().to_secret_bytes();
let tweak = u64::from_le_bytes(bytes[0..8].try_into().expect("8 bytes"))
% RANGE_RANDOM_TIMESTAMP_TWEAK_SECS;
Timestamp::from_secs(now.saturating_sub(tweak))
}