<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <updated>2026-04-03T02:24:49Z</updated>
  <generator>https://yabu.me</generator>

  <title>Nostr notes by src/get_file_hash_core/src/lib.rs</title>
  <author>
    <name>src/get_file_hash_core/src/lib.rs</name>
  </author>
  <link rel="self" type="application/atom+xml" href="https://yabu.me/npub1yud63kwep2w3t0jrmdrxgqzua43f67mwcvyj9eqjs93w32r05ywspu00v3.rss" />
  <link href="https://yabu.me/npub1yud63kwep2w3t0jrmdrxgqzua43f67mwcvyj9eqjs93w32r05ywspu00v3" />
  <id>https://yabu.me/npub1yud63kwep2w3t0jrmdrxgqzua43f67mwcvyj9eqjs93w32r05ywspu00v3</id>
  <icon>https://avatars.githubusercontent.com/u/135379339?s=400&amp;u=11cb72cccbc2b13252867099546074c50caef1ae&amp;v=4</icon>
  <logo>https://avatars.githubusercontent.com/u/135379339?s=400&amp;u=11cb72cccbc2b13252867099546074c50caef1ae&amp;v=4</logo>




  <entry>
    <id>https://yabu.me/nevent1qqs2arq80fgj8egd6mvsp4zh24tfd2fpdhvuzgp5t4hhtutqn2a5w5gzyqn3h2xemy9f69d7g0d5veqqtnkk98tmdmpsjghyz2qk969gd7s36a5c0wr</id>
    
      <title type="html">use std::process::Command; use std::path::PathBuf; #[cfg(feature ...</title>
    
    <link rel="alternate" href="https://yabu.me/nevent1qqs2arq80fgj8egd6mvsp4zh24tfd2fpdhvuzgp5t4hhtutqn2a5w5gzyqn3h2xemy9f69d7g0d5veqqtnkk98tmdmpsjghyz2qk969gd7s36a5c0wr" />
    <content type="html">
      use std::process::Command;&lt;br/&gt;use std::path::PathBuf;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use nostr_sdk::prelude::{*, EventBuilder, Tag, Kind};&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use serde_json::json;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use csv::ReaderBuilder;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use ::url::Url;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub use frost_secp256k1_tr as frost;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use frost::keys::{KeyPackage, PublicKeyPackage, SecretShare};&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use frost::round1::{SigningCommitments, SigningNonces};&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use frost::round2::SignatureShare;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use frost::SigningPackage;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use rand::thread_rng;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub use frost_secp256k1_tr as frost_bip340;&lt;br/&gt;&lt;br/&gt;pub mod frost_mailbox_logic;&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;use std::collections::BTreeMap;&lt;br/&gt;&lt;br/&gt;pub const DUMMY_BUILD_MANIFEST_ID_STR: &amp;amp;str = &amp;#34;f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0&amp;#34;;&lt;br/&gt;pub const DEFAULT_GNOSTR_KEY: &amp;amp;str = &amp;#34;e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855&amp;#34;;&lt;br/&gt;pub const DEFAULT_PICTURE_URL: &amp;amp;str = &amp;#34;&lt;a href=&#34;https://avatars.githubusercontent.com/u/135379339?s=400&amp;amp;u=11cb72cccbc2b13252867099546074c50caef1ae&amp;amp;v=4&amp;#34&#34;&gt;https://avatars.githubusercontent.com/u/135379339?s=400&amp;amp;u=11cb72cccbc2b13252867099546074c50caef1ae&amp;amp;v=4&amp;#34&lt;/a&gt;;;&lt;br/&gt;pub const DEFAULT_BANNER_URL: &amp;amp;str = &amp;#34;&lt;a href=&#34;https://raw.githubusercontent.com/gnostr-org/gnostr-icons/refs/heads/master/banner/1024x341.png&amp;#34&#34;&gt;https://raw.githubusercontent.com/gnostr-org/gnostr-icons/refs/heads/master/banner/1024x341.png&amp;#34&lt;/a&gt;;;&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;const ONLINE_RELAYS_GPS_CSV: &amp;amp;[u8] = include_bytes!(&amp;#34;online_relays_gps.csv&amp;#34;);&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub fn get_relay_urls() -&amp;gt; Vec&amp;lt;String&amp;gt; {&lt;br/&gt;    let content = String::from_utf8_lossy(ONLINE_RELAYS_GPS_CSV);&lt;br/&gt;    let mut rdr = ReaderBuilder::new()&lt;br/&gt;        .has_headers(true)&lt;br/&gt;        .from_reader(content.as_bytes());&lt;br/&gt;&lt;br/&gt;    rdr.records()&lt;br/&gt;        .filter_map(|result| {&lt;br/&gt;            match result {&lt;br/&gt;                Ok(record) =&amp;gt; {&lt;br/&gt;                    record.get(0).and_then(|url_str| {&lt;br/&gt;                        let full_url_str = if url_str.contains(&amp;#34;://&amp;#34;) {&lt;br/&gt;                            url_str.to_string()&lt;br/&gt;                        } else {&lt;br/&gt;                            format!(&amp;#34;wss://{}&amp;#34;, url_str)&lt;br/&gt;                        };&lt;br/&gt;                        match Url::parse(&amp;amp;full_url_str) {&lt;br/&gt;                            Ok(url) if url.scheme() == &amp;#34;wss&amp;#34; =&amp;gt; Some(url.to_string()),&lt;br/&gt;                            _ =&amp;gt; {&lt;br/&gt;                                eprintln!(&amp;#34;Warning: Invalid or unsupported relay URL scheme: {}&amp;#34;, full_url_str);&lt;br/&gt;                                None&lt;br/&gt;                            }&lt;br/&gt;                        }&lt;br/&gt;                    })&lt;br/&gt;                },&lt;br/&gt;                Err(e) =&amp;gt; {&lt;br/&gt;                    eprintln!(&amp;#34;Error reading CSV record: {}&amp;#34;, e);&lt;br/&gt;                    None&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;        })&lt;br/&gt;        .collect()&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/// Computes the SHA-256 hash of the specified file at compile time.&lt;br/&gt;///&lt;br/&gt;/// This macro takes a string literal representing a file path, reads the file&amp;#39;s bytes&lt;br/&gt;/// at compile time, computes its SHA-256 hash, and returns the hash as a hex-encoded `String`.&lt;br/&gt;///&lt;br/&gt;/// # Examples&lt;br/&gt;///&lt;br/&gt;/// ```rust&lt;br/&gt;/// use get_file_hash_core::get_file_hash;&lt;br/&gt;/// use sha2::{Digest, Sha256};&lt;br/&gt;///&lt;br/&gt;/// let hash = get_file_hash!(&amp;#34;lib.rs&amp;#34;);&lt;br/&gt;/// println!(&amp;#34;Hash: {}&amp;#34;, hash);&lt;br/&gt;/// ```&lt;br/&gt;&lt;br/&gt;#[macro_export]&lt;br/&gt;macro_rules! get_file_hash {&lt;br/&gt;    ($file_path:expr) =&amp;gt; {{&lt;br/&gt;        let bytes = include_bytes!($file_path);&lt;br/&gt;        let mut hasher = Sha256::new();&lt;br/&gt;        hasher.update(bytes);&lt;br/&gt;        let result = hasher.finalize();&lt;br/&gt;&lt;br/&gt;        // Convert the GenericArray to a hex string&lt;br/&gt;        result&lt;br/&gt;            .iter()&lt;br/&gt;            .map(|b| format!(&amp;#34;{:02x}&amp;#34;, b))&lt;br/&gt;            .collect::&amp;lt;String&amp;gt;()&lt;br/&gt;    }};&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/// Computes the SHA-256 hash of the specified file at compile time and uses it as a Nostr private key.&lt;br/&gt;///&lt;br/&gt;/// This macro takes a string literal representing a file path, computes its SHA-256 hash,&lt;br/&gt;/// and returns a `nostr::Keys` object derived from this hash.&lt;br/&gt;///&lt;br/&gt;/// # Examples&lt;br/&gt;///&lt;br/&gt;/// ```rust&lt;br/&gt;/// use get_file_hash_core::file_hash_as_nostr_private_key;&lt;br/&gt;/// use sha2::{Digest, Sha256};&lt;br/&gt;/// use nostr_sdk::prelude::ToBech32;&lt;br/&gt;///&lt;br/&gt;/// let keys = file_hash_as_nostr_private_key!(&amp;#34;lib.rs&amp;#34;);&lt;br/&gt;/// println!(&amp;#34;Public Key: {}&amp;#34;, keys.public_key().to_bech32().unwrap());&lt;br/&gt;/// ```&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;#[macro_export]&lt;br/&gt;macro_rules! file_hash_as_nostr_private_key {&lt;br/&gt;    ($file_path:expr) =&amp;gt; {{&lt;br/&gt;        let hash_hex = $crate::get_file_hash!($file_path);&lt;br/&gt;        nostr_sdk::Keys::parse(&amp;amp;hash_hex).expect(&amp;#34;Failed to create Nostr Keys from file hash&amp;#34;)&lt;br/&gt;    }};&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/// Publishes a NIP-34 repository announcement event to Nostr relays.&lt;br/&gt;///&lt;br/&gt;/// This macro takes Nostr keys, relay URLs, project details, a clone URL, and a file path.&lt;br/&gt;/// It computes the SHA-256 hash of the file at compile time to use as the &amp;#34;earliest unique commit&amp;#34; (EUC),&lt;br/&gt;/// and then publishes a Kind 30617 event.&lt;br/&gt;///&lt;br/&gt;/// # Examples&lt;br/&gt;///&lt;br/&gt;/// ```no_run&lt;br/&gt;/// use get_file_hash_core::repository_announcement;&lt;br/&gt;/// use get_file_hash_core::get_file_hash;&lt;br/&gt;/// use nostr_sdk::Keys;&lt;br/&gt;/// use sha2::{Digest, Sha256};&lt;br/&gt;///&lt;br/&gt;/// #[tokio::main]&lt;br/&gt;/// async fn main() {&lt;br/&gt;///     let keys = Keys::generate();&lt;br/&gt;///     let relay_urls = vec![&amp;#34;wss://relay.damus.io&amp;#34;.to_string()];&lt;br/&gt;///     let project_name = &amp;#34;my-awesome-repo&amp;#34;;&lt;br/&gt;///     let description = &amp;#34;A fantastic new project.&amp;#34;;&lt;br/&gt;///     let clone_url = &amp;#34;git@github.com:user/my-awesome-repo.git&amp;#34;;&lt;br/&gt;///&lt;br/&gt;///     repository_announcement!(&lt;br/&gt;///         &amp;amp;keys,&lt;br/&gt;///         &amp;amp;relay_urls,&lt;br/&gt;///         project_name,&lt;br/&gt;///         description,&lt;br/&gt;///         clone_url,&lt;br/&gt;///         &amp;#34;../Cargo.toml&amp;#34;, // Use a known file in your project&lt;br/&gt;///         None&lt;br/&gt;///     );&lt;br/&gt;/// }&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;#[macro_export]&lt;br/&gt;macro_rules! repository_announcement {&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $project_name:expr, $description:expr, $clone_url:expr, $file_for_euc:expr) =&amp;gt; {{&lt;br/&gt;        let euc_hash = $crate::get_file_hash!($file_for_euc);&lt;br/&gt;        // The &amp;#39;d&amp;#39; tag value should be unique for the repository. Using the project_name for simplicity.&lt;br/&gt;        let d_tag_value = $project_name;&lt;br/&gt;        $crate::publish_repository_announcement_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $project_name,&lt;br/&gt;            $description,&lt;br/&gt;            $clone_url,&lt;br/&gt;            &amp;amp;euc_hash,&lt;br/&gt;            d_tag_value,&lt;br/&gt;            None,&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $project_name:expr, $description:expr, $clone_url:expr, $file_for_euc:expr, $build_manifest_event_id:expr) =&amp;gt; {{&lt;br/&gt;        let euc_hash = $crate::get_file_hash!($file_for_euc);&lt;br/&gt;        let d_tag_value = $project_name;&lt;br/&gt;        $crate::publish_repository_announcement_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $project_name,&lt;br/&gt;            $description,&lt;br/&gt;            $clone_url,&lt;br/&gt;            &amp;amp;euc_hash,&lt;br/&gt;            d_tag_value,&lt;br/&gt;            $build_manifest_event_id, // Correct: Pass directly&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/// Publishes a NIP-34 patch event to Nostr relays.&lt;br/&gt;///&lt;br/&gt;/// This macro takes Nostr keys, relay URLs, the repository&amp;#39;s d-tag value,&lt;br/&gt;/// the commit ID the patch applies to, and the path to the patch file.&lt;br/&gt;/// The content of the patch file is included directly in the event.&lt;br/&gt;///&lt;br/&gt;/// # Examples&lt;br/&gt;///&lt;br/&gt;/// ```no_run&lt;br/&gt;/// use get_file_hash_core::publish_patch;&lt;br/&gt;/// use nostr_sdk::Keys;&lt;br/&gt;///&lt;br/&gt;/// #[tokio::main]&lt;br/&gt;/// async fn main() {&lt;br/&gt;///     let keys = Keys::generate();&lt;br/&gt;///     let relay_urls = vec![&amp;#34;wss://relay.damus.io&amp;#34;.to_string()];&lt;br/&gt;///     let d_tag = &amp;#34;my-awesome-repo&amp;#34;;&lt;br/&gt;///     let commit_id = &amp;#34;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0&amp;#34;; // Example commit ID&lt;br/&gt;///&lt;br/&gt;///     publish_patch!(&lt;br/&gt;///         &amp;amp;keys,&lt;br/&gt;///         &amp;amp;relay_urls,&lt;br/&gt;///         d_tag,&lt;br/&gt;///         commit_id,&lt;br/&gt;///         &amp;#34;lib.rs&amp;#34; // Use an existing file for the patch content&lt;br/&gt;///     );&lt;br/&gt;/// }&lt;br/&gt;/// ```&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;#[macro_export]&lt;br/&gt;macro_rules! publish_patch {&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $commit_id:expr, $patch_file_path:expr) =&amp;gt; {{&lt;br/&gt;        let patch_content = include_str!($patch_file_path);&lt;br/&gt;        $crate::publish_patch_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $d_tag_value,&lt;br/&gt;            $commit_id,&lt;br/&gt;            patch_content,&lt;br/&gt;            None, // Pass None for build_manifest_event_id&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $commit_id:expr, $patch_file_path:expr, $build_manifest_event_id:expr) =&amp;gt; {{&lt;br/&gt;        let patch_content = include_str!($patch_file_path);&lt;br/&gt;        $crate::publish_patch_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $d_tag_value,&lt;br/&gt;            $commit_id,&lt;br/&gt;            patch_content,&lt;br/&gt;            $build_manifest_event_id, // Pass directly, macro arg should be Option&amp;lt;&amp;amp;EventId&amp;gt;&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/// Publishes a NIP-34 pull request event to Nostr relays.&lt;br/&gt;///&lt;br/&gt;/// This macro takes Nostr keys, relay URLs, the repository&amp;#39;s d-tag value,&lt;br/&gt;/// the commit ID of the pull request, a clone URL where the work can be fetched,&lt;br/&gt;/// and an optional title for the pull request.&lt;br/&gt;///&lt;br/&gt;/// # Examples&lt;br/&gt;///&lt;br/&gt;/// ```no_run&lt;br/&gt;/// use get_file_hash_core::publish_pull_request;&lt;br/&gt;/// use nostr_sdk::Keys;&lt;br/&gt;///&lt;br/&gt;/// #[tokio::main]&lt;br/&gt;/// async fn main() {&lt;br/&gt;///     let keys = Keys::generate();&lt;br/&gt;///     let relay_urls = vec![&amp;#34;wss://relay.damus.io&amp;#34;.to_string()];&lt;br/&gt;///     let d_tag = &amp;#34;my-awesome-repo&amp;#34;;&lt;br/&gt;///     let commit_id = &amp;#34;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0&amp;#34;;&lt;br/&gt;///     let clone_url = &amp;#34;git@github.com:user/my-feature-branch.git&amp;#34;;&lt;br/&gt;///     let title = Some(&amp;#34;Feat: Add new awesome feature&amp;#34;);&lt;br/&gt;///&lt;br/&gt;///     publish_pull_request!(&lt;br/&gt;///         &amp;amp;keys,&lt;br/&gt;///         &amp;amp;relay_urls,&lt;br/&gt;///         d_tag,&lt;br/&gt;///         commit_id,&lt;br/&gt;///         clone_url,&lt;br/&gt;///         title,&lt;br/&gt;///         None&lt;br/&gt;///     );&lt;br/&gt;/// }&lt;br/&gt;/// ```&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;#[macro_export]&lt;br/&gt;macro_rules! publish_pull_request {&lt;br/&gt;    // 5 args: No title, no build_manifest_event_id&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $commit_id:expr, $clone_url:expr) =&amp;gt; {{&lt;br/&gt;        $crate::publish_pull_request_event(&lt;br/&gt;            $keys, $relay_urls, $d_tag_value, $commit_id, $clone_url,&lt;br/&gt;            None, // title: Option&amp;lt;&amp;amp;str&amp;gt;&lt;br/&gt;            None, // build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;&lt;br/&gt;    // 6 args: With title (Option&amp;lt;&amp;amp;str&amp;gt;), no build_manifest_event_id&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $commit_id:expr, $clone_url:expr, $title:expr) =&amp;gt; {{&lt;br/&gt;        $crate::publish_pull_request_event(&lt;br/&gt;            $keys, $relay_urls, $d_tag_value, $commit_id, $clone_url,&lt;br/&gt;            $title, // title: Option&amp;lt;&amp;amp;str&amp;gt;&lt;br/&gt;            None, // build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;&lt;br/&gt;    // 7 args: With title (Option&amp;lt;&amp;amp;str&amp;gt;), with build_manifest_event_id (Option&amp;lt;&amp;amp;EventId&amp;gt;)&lt;br/&gt;    // This needs to be before the 6-arg arm that passes a single Option for build_manifest_event_id if it&amp;#39;s not None.&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $commit_id:expr, $clone_url:expr, $title:expr, $build_manifest_event_id:expr) =&amp;gt; {{&lt;br/&gt;        $crate::publish_pull_request_event(&lt;br/&gt;            $keys, $relay_urls, $d_tag_value, $commit_id, $clone_url,&lt;br/&gt;            $title, // title: Option&amp;lt;&amp;amp;str&amp;gt;&lt;br/&gt;            $build_manifest_event_id, // build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;&lt;br/&gt;    // 6 args: No title, with build_manifest_event_id (Option&amp;lt;&amp;amp;EventId&amp;gt;)&lt;br/&gt;    // This must be after the 7-arg arm to avoid ambiguity.&lt;br/&gt;    // The example needs to explicitly pass None for title.&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $commit_id:expr, $clone_url:expr, _none_title:tt, $build_manifest_event_id:expr) =&amp;gt; {{ // _none_title as tt to match None&lt;br/&gt;        $crate::publish_pull_request_event(&lt;br/&gt;            $keys, $relay_urls, $d_tag_value, $commit_id, $clone_url,&lt;br/&gt;            None, // title: Option&amp;lt;&amp;amp;str&amp;gt;&lt;br/&gt;            $build_manifest_event_id, // build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/// Publishes a NIP-34 PR update event to Nostr relays.&lt;br/&gt;///&lt;br/&gt;/// This macro takes Nostr keys, relay URLs, the repository&amp;#39;s d-tag value,&lt;br/&gt;/// the event ID of the original pull request, the new commit ID,&lt;br/&gt;/// and the new clone URL.&lt;br/&gt;///&lt;br/&gt;/// # Examples&lt;br/&gt;///&lt;br/&gt;/// ```no_run&lt;br/&gt;/// use get_file_hash_core::publish_pr_update;&lt;br/&gt;/// use nostr_sdk::Keys;&lt;br/&gt;/// use nostr_sdk::EventId;&lt;br/&gt;/// use std::str::FromStr;&lt;br/&gt;///&lt;br/&gt;/// #[tokio::main]&lt;br/&gt;/// async fn main() {&lt;br/&gt;///     let keys = Keys::generate();&lt;br/&gt;///     let relay_urls = vec![&amp;#34;wss://relay.damus.io&amp;#34;.to_string()];&lt;br/&gt;///     let d_tag = &amp;#34;my-awesome-repo&amp;#34;;&lt;br/&gt;///     let pr_event_id = EventId::from_str(&amp;#34;f6e4d6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9&amp;#34;).unwrap(); // Example PR Event ID&lt;br/&gt;///     let updated_commit_id = &amp;#34;z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0&amp;#34;;&lt;br/&gt;///     let updated_clone_url = &amp;#34;git@github.com:user/my-feature-branch-v2.git&amp;#34;;&lt;br/&gt;///&lt;br/&gt;///     publish_pr_update!(&lt;br/&gt;///         &amp;amp;keys,&lt;br/&gt;///         &amp;amp;relay_urls,&lt;br/&gt;///         d_tag,&lt;br/&gt;///         &amp;amp;pr_event_id,&lt;br/&gt;///         updated_commit_id,&lt;br/&gt;///         updated_clone_url&lt;br/&gt;///     );&lt;br/&gt;/// }&lt;br/&gt;/// ```&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;#[macro_export]&lt;br/&gt;macro_rules! publish_pr_update {&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $pr_event_id:expr, $updated_commit_id:expr, $updated_clone_url:expr) =&amp;gt; {{&lt;br/&gt;        $crate::publish_pr_update_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $d_tag_value,&lt;br/&gt;            $pr_event_id,&lt;br/&gt;            $updated_commit_id,&lt;br/&gt;            $updated_clone_url,&lt;br/&gt;            None, // Pass None for build_manifest_event_id&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $pr_event_id:expr, $updated_commit_id:expr, $updated_clone_url:expr, $build_manifest_event_id:expr) =&amp;gt; {{&lt;br/&gt;        $crate::publish_pr_update_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $d_tag_value,&lt;br/&gt;            $pr_event_id,&lt;br/&gt;            $updated_commit_id,&lt;br/&gt;            $updated_clone_url,&lt;br/&gt;            $build_manifest_event_id,&lt;br/&gt;&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/// Publishes a NIP-34 repository state event to Nostr relays.&lt;br/&gt;///&lt;br/&gt;/// This macro takes Nostr keys, relay URLs, the repository&amp;#39;s d-tag value,&lt;br/&gt;/// the branch name, and the commit ID for that branch.&lt;br/&gt;///&lt;br/&gt;/// # Examples&lt;br/&gt;///&lt;br/&gt;/// ```no_run&lt;br/&gt;/// use get_file_hash_core::publish_repository_state;&lt;br/&gt;/// use nostr_sdk::Keys;&lt;br/&gt;///&lt;br/&gt;/// #[tokio::main]&lt;br/&gt;/// async fn main() {&lt;br/&gt;///     let keys = Keys::generate();&lt;br/&gt;///     let relay_urls = vec![&amp;#34;wss://relay.damus.io&amp;#34;.to_string()];&lt;br/&gt;///     let d_tag = &amp;#34;my-awesome-repo&amp;#34;;&lt;br/&gt;///     let branch_name = &amp;#34;main&amp;#34;;&lt;br/&gt;///     let commit_id = &amp;#34;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0&amp;#34;;&lt;br/&gt;///&lt;br/&gt;///     publish_repository_state!(&lt;br/&gt;///         &amp;amp;keys,&lt;br/&gt;///         &amp;amp;relay_urls,&lt;br/&gt;///         d_tag,&lt;br/&gt;///         branch_name,&lt;br/&gt;///         commit_id&lt;br/&gt;///     );&lt;br/&gt;/// }&lt;br/&gt;/// ```&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;#[macro_export]&lt;br/&gt;macro_rules! publish_repository_state {&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $branch_name:expr, $commit_id:expr) =&amp;gt; {{&lt;br/&gt;        $crate::publish_repository_state_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $d_tag_value,&lt;br/&gt;            $branch_name,&lt;br/&gt;            $commit_id,&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/// Publishes a NIP-34 issue event to Nostr relays.&lt;br/&gt;///&lt;br/&gt;/// This macro takes Nostr keys, relay URLs, the repository&amp;#39;s d-tag value,&lt;br/&gt;/// a unique issue ID, the issue&amp;#39;s title, and its content (markdown).&lt;br/&gt;///&lt;br/&gt;/// # Examples&lt;br/&gt;///&lt;br/&gt;/// ```no_run&lt;br/&gt;/// use get_file_hash_core::publish_issue;&lt;br/&gt;/// use nostr_sdk::Keys;&lt;br/&gt;///&lt;br/&gt;/// #[tokio::main]&lt;br/&gt;/// async fn main() {&lt;br/&gt;///     let keys = Keys::generate();&lt;br/&gt;///     let relay_urls = vec![&amp;#34;wss://relay.damus.io&amp;#34;.to_string()];&lt;br/&gt;///     let d_tag = &amp;#34;my-awesome-repo&amp;#34;;&lt;br/&gt;///     let issue_id = &amp;#34;123&amp;#34;;&lt;br/&gt;///     let title = &amp;#34;Bug: Fix authentication flow&amp;#34;;&lt;br/&gt;///     let content = &amp;#34;The authentication flow is currently broken when users try to log in with invalid credentials. It crashes instead of showing an error message.&amp;#34;;&lt;br/&gt;///&lt;br/&gt;///     publish_issue!(&lt;br/&gt;///         &amp;amp;keys,&lt;br/&gt;///         &amp;amp;relay_urls,&lt;br/&gt;///         d_tag,&lt;br/&gt;///         issue_id,&lt;br/&gt;///         title,&lt;br/&gt;///         content&lt;br/&gt;///     );&lt;br/&gt;/// }&lt;br/&gt;/// ```&lt;br/&gt;/// ```&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;#[macro_export]&lt;br/&gt;macro_rules! publish_issue {&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $issue_id:expr, $title:expr, $content:expr) =&amp;gt; {{&lt;br/&gt;        $crate::publish_issue_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $d_tag_value,&lt;br/&gt;            $issue_id,&lt;br/&gt;            $title,&lt;br/&gt;            $content,&lt;br/&gt;            None, // Pass None for build_manifest_event_id&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;    ($keys:expr, $relay_urls:expr, $d_tag_value:expr, $issue_id:expr, $title:expr, $content:expr, $build_manifest_event_id:expr) =&amp;gt; {{&lt;br/&gt;        $crate::publish_issue_event(&lt;br/&gt;            $keys,&lt;br/&gt;            $relay_urls,&lt;br/&gt;            $d_tag_value,&lt;br/&gt;            $issue_id,&lt;br/&gt;            $title,&lt;br/&gt;            $content,&lt;br/&gt;            $build_manifest_event_id, // Pass Option&amp;lt;&amp;amp;EventId&amp;gt; directly&lt;br/&gt;        ).await;&lt;br/&gt;    }};&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;pub fn get_git_tracked_files(dir: &amp;amp;PathBuf) -&amp;gt; Vec&amp;lt;String&amp;gt; {&lt;br/&gt;    match Command::new(&amp;#34;git&amp;#34;)&lt;br/&gt;        .arg(&amp;#34;ls-files&amp;#34;)&lt;br/&gt;        .current_dir(dir)&lt;br/&gt;        .stdout(std::process::Stdio::piped())&lt;br/&gt;        .stderr(std::process::Stdio::null())&lt;br/&gt;        .output()&lt;br/&gt;    {&lt;br/&gt;        Ok(output) if output.status.success() &amp;amp;&amp;amp; !output.stdout.is_empty() =&amp;gt; {&lt;br/&gt;            String::from_utf8_lossy(&amp;amp;output.stdout)&lt;br/&gt;                .lines()&lt;br/&gt;                .filter_map(|line| Some(String::from(line)))&lt;br/&gt;                .collect()&lt;br/&gt;        }&lt;br/&gt;        Ok(output) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=git ls-files failed or returned empty. Status: {:?}, Stderr: {}&amp;#34;, &lt;br/&gt;                     output.status, String::from_utf8_lossy(&amp;amp;output.stderr));&lt;br/&gt;            Vec::new()&lt;br/&gt;        }&lt;br/&gt;        Err(e) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to execute git ls-files: {}&amp;#34;, e);&lt;br/&gt;            Vec::new()&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub async fn publish_metadata_event(&lt;br/&gt;    keys: &amp;amp;Keys,&lt;br/&gt;    relay_urls: &amp;amp;[String],&lt;br/&gt;    picture_url: &amp;amp;str,&lt;br/&gt;    banner_url: &amp;amp;str,&lt;br/&gt;    file_path_str: &amp;amp;str,&lt;br/&gt;) {&lt;br/&gt;    let client = nostr_sdk::Client::new(keys.clone());&lt;br/&gt;&lt;br/&gt;    for relay_url in relay_urls {&lt;br/&gt;        if let Err(e) = client.add_relay(relay_url).await {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to add relay for metadata {}: {}&amp;#34;, relay_url, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    client.connect().await;&lt;br/&gt;&lt;br/&gt;    let metadata_json = json!({&lt;br/&gt;        &amp;#34;picture&amp;#34;: picture_url,&lt;br/&gt;        &amp;#34;banner&amp;#34;: banner_url,&lt;br/&gt;        &amp;#34;name&amp;#34;: file_path_str,&lt;br/&gt;        &amp;#34;about&amp;#34;: format!(&amp;#34;Metadata for file event: {}&amp;#34;, file_path_str),&lt;br/&gt;    });&lt;br/&gt;&lt;br/&gt;    let metadata = serde_json::from_str::&amp;lt;nostr_sdk::Metadata&amp;gt;(&amp;amp;metadata_json.to_string())&lt;br/&gt;        .expect(&amp;#34;Failed to parse metadata JSON&amp;#34;);&lt;br/&gt;&lt;br/&gt;    match client.send_event_builder(EventBuilder::metadata(&amp;amp;metadata)).await {&lt;br/&gt;        Ok(event_id) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Published Nostr metadata event for {}: {:?}&amp;#34;, file_path_str, event_id);&lt;br/&gt;        }&lt;br/&gt;        Err(e) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to publish Nostr metadata event for {}: {}&amp;#34;, file_path_str, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub async fn publish_repository_announcement_event(&lt;br/&gt;    keys: &amp;amp;Keys,&lt;br/&gt;    relay_urls: &amp;amp;[String],&lt;br/&gt;    project_name: &amp;amp;str,&lt;br/&gt;    description: &amp;amp;str,&lt;br/&gt;    clone_url: &amp;amp;str,&lt;br/&gt;    euc: &amp;amp;str, // Earliest Unique Commit hash&lt;br/&gt;    d_tag_value: &amp;amp;str, // d-tag value&lt;br/&gt;    build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;,&lt;br/&gt;) {&lt;br/&gt;    let client = nostr_sdk::Client::new(keys.clone());&lt;br/&gt;&lt;br/&gt;    for relay_url in relay_urls {&lt;br/&gt;        if let Err(e) = client.add_relay(relay_url).await {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to add relay for repository announcement {}: {}&amp;#34;, relay_url, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    client.connect().await;&lt;br/&gt;&lt;br/&gt;    let mut tags = vec![&lt;br/&gt;        Tag::parse([&amp;#34;name&amp;#34;, project_name]).expect(&amp;#34;Failed to create name tag&amp;#34;),&lt;br/&gt;        Tag::parse([&amp;#34;description&amp;#34;, description]).expect(&amp;#34;Failed to create description tag&amp;#34;),&lt;br/&gt;        Tag::parse([&amp;#34;clone&amp;#34;, clone_url]).expect(&amp;#34;Failed to create clone tag&amp;#34;),&lt;br/&gt;        Tag::custom(&amp;#34;euc&amp;#34;.into(), vec![euc.to_string()]),&lt;br/&gt;        Tag::custom(&amp;#34;d&amp;#34;.into(), vec![d_tag_value.to_string()]), // NIP-33 d-tag&lt;br/&gt;    ];&lt;br/&gt;&lt;br/&gt;    if let Some(event_id) = build_manifest_event_id {&lt;br/&gt;        tags.push(Tag::event(*event_id));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    let event_builder = EventBuilder::new(&lt;br/&gt;        Kind::Custom(30617), // NIP-34 Repository Announcement kind&lt;br/&gt;        &amp;#34;&amp;#34;, // Content is empty for repository announcement&lt;br/&gt;    ).tags(tags);&lt;br/&gt;&lt;br/&gt;    match client.send_event_builder(event_builder).await {&lt;br/&gt;        Ok(event_id) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Published NIP-34 Repository Announcement for {}. Event ID (raw): {:?}, Event ID (bech32): {}&amp;#34;, project_name, event_id, event_id.to_bech32().unwrap());&lt;br/&gt;        }&lt;br/&gt;        Err(e) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to publish NIP-34 Repository Announcement for {}: {}&amp;#34;, project_name, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub async fn publish_patch_event(&lt;br/&gt;    keys: &amp;amp;Keys,&lt;br/&gt;    relay_urls: &amp;amp;[String],&lt;br/&gt;    d_tag_value: &amp;amp;str,&lt;br/&gt;    commit_id: &amp;amp;str,&lt;br/&gt;    patch_content: &amp;amp;str,&lt;br/&gt;    build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;,&lt;br/&gt;) {&lt;br/&gt;    let client = nostr_sdk::Client::new(keys.clone());&lt;br/&gt;&lt;br/&gt;    for relay_url in relay_urls {&lt;br/&gt;        if let Err(e) = client.add_relay(relay_url).await {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to add relay for patch {}: {}&amp;#34;, relay_url, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    client.connect().await;&lt;br/&gt;&lt;br/&gt;    let mut tags = vec![&lt;br/&gt;        Tag::custom(&amp;#34;d&amp;#34;.into(), vec![d_tag_value.to_string()]), // Repository d-tag&lt;br/&gt;        Tag::parse([&amp;#34;commit&amp;#34;, commit_id]).expect(&amp;#34;Failed to create commit tag&amp;#34;),&lt;br/&gt;    ];&lt;br/&gt;&lt;br/&gt;    if let Some(event_id) = build_manifest_event_id {&lt;br/&gt;        tags.push(Tag::event(*event_id));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    let event_builder = EventBuilder::new(&lt;br/&gt;        Kind::Custom(1617), // NIP-34 Patch kind&lt;br/&gt;        patch_content,&lt;br/&gt;    ).tags(tags);&lt;br/&gt;&lt;br/&gt;    match client.send_event_builder(event_builder).await {&lt;br/&gt;        Ok(event_id) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Published NIP-34 Patch event for commit {}. Event ID (raw): {:?}, Event ID (bech32): {}&amp;#34;, commit_id, event_id, event_id.to_bech32().unwrap());&lt;br/&gt;        }&lt;br/&gt;        Err(e) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to publish NIP-34 Patch event for commit {}: {}&amp;#34;, commit_id, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub async fn publish_pull_request_event(&lt;br/&gt;    keys: &amp;amp;Keys,&lt;br/&gt;    relay_urls: &amp;amp;[String],&lt;br/&gt;    d_tag_value: &amp;amp;str,&lt;br/&gt;    commit_id: &amp;amp;str,&lt;br/&gt;    clone_url: &amp;amp;str,&lt;br/&gt;    title: Option&amp;lt;&amp;amp;str&amp;gt;,&lt;br/&gt;    build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;,&lt;br/&gt;) {&lt;br/&gt;    let client = nostr_sdk::Client::new(keys.clone());&lt;br/&gt;&lt;br/&gt;    for relay_url in relay_urls {&lt;br/&gt;        if let Err(e) = client.add_relay(relay_url).await {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to add relay for pull request {}: {}&amp;#34;, relay_url, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    client.connect().await;&lt;br/&gt;&lt;br/&gt;    let mut tags = vec![&lt;br/&gt;        Tag::custom(&amp;#34;d&amp;#34;.into(), vec![d_tag_value.to_string()]), // Repository d-tag&lt;br/&gt;        Tag::parse([&amp;#34;commit&amp;#34;, commit_id]).expect(&amp;#34;Failed to create commit tag&amp;#34;),&lt;br/&gt;        Tag::parse([&amp;#34;clone&amp;#34;, clone_url]).expect(&amp;#34;Failed to create clone tag&amp;#34;),&lt;br/&gt;    ];&lt;br/&gt;&lt;br/&gt;    if let Some(t) = title {&lt;br/&gt;        tags.push(Tag::parse([&amp;#34;title&amp;#34;, t]).expect(&amp;#34;Failed to create title tag&amp;#34;));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    if let Some(event_id) = build_manifest_event_id {&lt;br/&gt;        tags.push(Tag::event(*event_id));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    let event_builder = EventBuilder::new(&lt;br/&gt;        Kind::Custom(1618), // NIP-34 Pull Request kind&lt;br/&gt;        &amp;#34;&amp;#34;, // Content can be empty or a description for the PR&lt;br/&gt;    ).tags(tags);&lt;br/&gt;&lt;br/&gt;    match client.send_event_builder(event_builder).await {&lt;br/&gt;        Ok(event_id) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Published NIP-34 Pull Request event for commit {}. Event ID (raw): {:?}, Event ID (bech32): {}&amp;#34;, commit_id, event_id, event_id.to_bech32().unwrap());&lt;br/&gt;        }&lt;br/&gt;        Err(e) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to publish NIP-34 Pull Request event for commit {}: {}&amp;#34;, commit_id, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub async fn publish_pr_update_event(&lt;br/&gt;    keys: &amp;amp;Keys,&lt;br/&gt;    relay_urls: &amp;amp;[String],&lt;br/&gt;    d_tag_value: &amp;amp;str,&lt;br/&gt;    pr_event_id: &amp;amp;EventId,&lt;br/&gt;    updated_commit_id: &amp;amp;str,&lt;br/&gt;    updated_clone_url: &amp;amp;str,&lt;br/&gt;    build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;,&lt;br/&gt;) {&lt;br/&gt;    let client = nostr_sdk::Client::new(keys.clone());&lt;br/&gt;&lt;br/&gt;    for relay_url in relay_urls {&lt;br/&gt;        if let Err(e) = client.add_relay(relay_url).await {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to add relay for PR update {}: {}&amp;#34;, relay_url, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    client.connect().await;&lt;br/&gt;&lt;br/&gt;    let mut tags = vec![&lt;br/&gt;        Tag::custom(&amp;#34;d&amp;#34;.into(), vec![d_tag_value.to_string()]), // Repository d-tag&lt;br/&gt;        Tag::parse([&amp;#34;p&amp;#34;, pr_event_id.to_string().as_str()]).expect(&amp;#34;Failed to create PR event ID tag&amp;#34;),&lt;br/&gt;        Tag::parse([&amp;#34;commit&amp;#34;, updated_commit_id]).expect(&amp;#34;Failed to create updated commit ID tag&amp;#34;),&lt;br/&gt;        Tag::parse([&amp;#34;clone&amp;#34;, updated_clone_url]).expect(&amp;#34;Failed to create updated clone URL tag&amp;#34;),&lt;br/&gt;    ];&lt;br/&gt;&lt;br/&gt;    if let Some(event_id) = build_manifest_event_id {&lt;br/&gt;        tags.push(Tag::event(*event_id));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    let event_builder = EventBuilder::new(&lt;br/&gt;        Kind::Custom(1619), // NIP-34 PR Update kind&lt;br/&gt;        &amp;#34;&amp;#34;, // Content is empty for PR update&lt;br/&gt;    ).tags(tags);&lt;br/&gt;&lt;br/&gt;    match client.send_event_builder(event_builder).await {&lt;br/&gt;        Ok(event_id) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Published NIP-34 PR Update event for PR {} (raw: {:?}). Event ID (raw): {:?}, Event ID (bech32): {}&amp;#34;, pr_event_id.to_bech32().unwrap(), pr_event_id, event_id, event_id.to_bech32().unwrap());&lt;br/&gt;        }&lt;br/&gt;        Err(e) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to publish NIP-34 PR Update event for PR {}: {}&amp;#34;, pr_event_id.to_string(), e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub async fn publish_repository_state_event(&lt;br/&gt;    keys: &amp;amp;Keys,&lt;br/&gt;    relay_urls: &amp;amp;[String],&lt;br/&gt;    d_tag_value: &amp;amp;str,&lt;br/&gt;    branch_name: &amp;amp;str,&lt;br/&gt;    commit_id: &amp;amp;str,&lt;br/&gt;) {&lt;br/&gt;    let client = nostr_sdk::Client::new(keys.clone());&lt;br/&gt;&lt;br/&gt;    for relay_url in relay_urls {&lt;br/&gt;        if let Err(e) = client.add_relay(relay_url).await {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to add relay for repository state {}: {}&amp;#34;, relay_url, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    client.connect().await;&lt;br/&gt;&lt;br/&gt;    let event_builder = EventBuilder::new(&lt;br/&gt;        Kind::Custom(30618), // NIP-34 Repository State kind&lt;br/&gt;        &amp;#34;&amp;#34;, // Content is empty for repository state&lt;br/&gt;    ).tags(vec![&lt;br/&gt;        Tag::custom(&amp;#34;d&amp;#34;.into(), vec![d_tag_value.to_string()]), // Repository d-tag&lt;br/&gt;        Tag::parse([&amp;#34;name&amp;#34;, branch_name]).expect(&amp;#34;Failed to create branch name tag&amp;#34;),&lt;br/&gt;        Tag::parse([&amp;#34;commit&amp;#34;, commit_id]).expect(&amp;#34;Failed to create commit ID tag&amp;#34;),&lt;br/&gt;    ]);&lt;br/&gt;&lt;br/&gt;    match client.send_event_builder(event_builder).await {&lt;br/&gt;        Ok(event_id) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Published NIP-34 Repository State event for branch {} (commit {}). Event ID (raw): {:?}, Event ID (bech32): {}&amp;#34;, branch_name, commit_id, event_id, event_id.to_bech32().unwrap());&lt;br/&gt;        }&lt;br/&gt;        Err(e) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to publish NIP-34 Repository State event for branch {} (commit {}): {}&amp;#34;, branch_name, commit_id, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub async fn publish_issue_event(&lt;br/&gt;    keys: &amp;amp;Keys,&lt;br/&gt;    relay_urls: &amp;amp;[String],&lt;br/&gt;    d_tag_value: &amp;amp;str,&lt;br/&gt;    issue_id: &amp;amp;str, // Unique identifier for the issue&lt;br/&gt;    title: &amp;amp;str,&lt;br/&gt;    content: &amp;amp;str,&lt;br/&gt;    build_manifest_event_id: Option&amp;lt;&amp;amp;EventId&amp;gt;,&lt;br/&gt;) {&lt;br/&gt;    let client = nostr_sdk::Client::new(keys.clone());&lt;br/&gt;&lt;br/&gt;    for relay_url in relay_urls {&lt;br/&gt;        if let Err(e) = client.add_relay(relay_url).await {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to add relay for issue {}: {}&amp;#34;, relay_url, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    client.connect().await;&lt;br/&gt;&lt;br/&gt;    let mut tags = vec![&lt;br/&gt;        Tag::custom(&amp;#34;d&amp;#34;.into(), vec![d_tag_value.to_string()]), // Repository d-tag&lt;br/&gt;        Tag::parse([&amp;#34;i&amp;#34;, issue_id]).expect(&amp;#34;Failed to create issue ID tag&amp;#34;),&lt;br/&gt;        Tag::parse([&amp;#34;title&amp;#34;, title]).expect(&amp;#34;Failed to create title tag&amp;#34;),&lt;br/&gt;    ];&lt;br/&gt;&lt;br/&gt;    if let Some(event_id) = build_manifest_event_id {&lt;br/&gt;        tags.push(Tag::event(*event_id));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    let event_builder = EventBuilder::new(&lt;br/&gt;        Kind::Custom(1621), // NIP-34 Issue kind&lt;br/&gt;        content,&lt;br/&gt;    ).tags(tags);&lt;br/&gt;&lt;br/&gt;    match client.send_event_builder(event_builder).await {&lt;br/&gt;        Ok(event_id) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Published NIP-34 Issue event for issue {} ({}). Event ID (raw): {:?}, Event ID (bech32): {}&amp;#34;, issue_id, title, event_id, event_id.to_bech32().unwrap());&lt;br/&gt;        }&lt;br/&gt;        Err(e) =&amp;gt; {&lt;br/&gt;            println!(&amp;#34;cargo:warning=Failed to publish NIP-34 Issue event for issue {} ({}): {}&amp;#34;, issue_id, title, e);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;#[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;pub fn generate_frost_keys(&lt;br/&gt;    max_signers: u16,&lt;br/&gt;    min_signers: u16,&lt;br/&gt;) -&amp;gt; Result&amp;lt;(BTreeMap&amp;lt;frost::Identifier, SecretShare&amp;gt;, PublicKeyPackage), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {                let mut rng = thread_rng();&lt;br/&gt;    let (shares, pubkey_package) = frost::keys::generate_with_dealer(&lt;br/&gt;        max_signers,&lt;br/&gt;        min_signers,&lt;br/&gt;        frost::keys::IdentifierList::Default,&lt;br/&gt;        &amp;amp;mut rng,&lt;br/&gt;    )?;&lt;br/&gt;    Ok((shares, pubkey_package))&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    pub fn create_frost_commitment(&lt;br/&gt;    secret_share: &amp;amp;SecretShare,&lt;br/&gt;    ) -&amp;gt; (SigningNonces, SigningCommitments) {&lt;br/&gt;    let mut rng = thread_rng();&lt;br/&gt;    frost::round1::commit(secret_share.signing_share(), &amp;amp;mut rng)&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    pub fn create_signing_package(&lt;br/&gt;    commitments: BTreeMap&amp;lt;frost::Identifier, SigningCommitments&amp;gt;,&lt;br/&gt;    message: &amp;amp;[u8],&lt;br/&gt;    ) -&amp;gt; SigningPackage {&lt;br/&gt;    frost::SigningPackage::new(commitments, message)&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    pub fn generate_signature_share(&lt;br/&gt;    signing_package: &amp;amp;SigningPackage,&lt;br/&gt;    nonces: &amp;amp;SigningNonces,&lt;br/&gt;    secret_share: &amp;amp;SecretShare,&lt;br/&gt;    ) -&amp;gt; Result&amp;lt;SignatureShare, Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {&lt;br/&gt;    let key_package: KeyPackage = secret_share.clone().try_into()?;&lt;br/&gt;    Ok(frost::round2::sign(signing_package, nonces, &amp;amp;key_package)?)&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    pub fn aggregate_signature_shares(&lt;br/&gt;    signing_package: &amp;amp;SigningPackage,&lt;br/&gt;    signature_shares: &amp;amp;BTreeMap&amp;lt;frost::Identifier, SignatureShare&amp;gt;,&lt;br/&gt;    pubkey_package: &amp;amp;PublicKeyPackage,&lt;br/&gt;    ) -&amp;gt; Result&amp;lt;frost_secp256k1_tr::Signature, Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {&lt;br/&gt;    Ok(frost::aggregate(signing_package, signature_shares, pubkey_package)?)&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    pub fn verify_frost_signature(&lt;br/&gt;    group_public_key: &amp;amp;frost_secp256k1_tr::VerifyingKey,&lt;br/&gt;    message: &amp;amp;[u8],&lt;br/&gt;    signature: &amp;amp;frost_secp256k1_tr::Signature,&lt;br/&gt;    ) -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {&lt;br/&gt;    Ok(group_public_key.verify(message, signature)?)&lt;br/&gt;    }&lt;br/&gt;#[cfg(test)]&lt;br/&gt;mod tests {&lt;br/&gt;	use serial_test::serial;&lt;br/&gt;&lt;br/&gt;	use std::collections::BTreeMap;&lt;br/&gt;    use std::fs::File;&lt;br/&gt;    use std::io::Write;&lt;br/&gt;    use sha2::{Digest, Sha256};&lt;br/&gt;    use tempfile;&lt;br/&gt;    use super::get_git_tracked_files;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    use super::frost;&lt;br/&gt;        use std::process::Command;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    use nostr_sdk::EventId;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    use std::str::FromStr;&lt;br/&gt;&lt;br/&gt;    // Test for get_file_hash! macro&lt;br/&gt;    #[test]&lt;br/&gt;    fn test_get_file_hash() {&lt;br/&gt;        let dir = tempfile::tempdir().unwrap();&lt;br/&gt;        let file_path = dir.path().join(&amp;#34;test_file.txt&amp;#34;);&lt;br/&gt;        let content = &amp;#34;Hello, world!&amp;#34;;&lt;br/&gt;        File::create(&amp;amp;file_path).unwrap().write_all(content.as_bytes()).unwrap();&lt;br/&gt;&lt;br/&gt;        // The macro expects a string literal, so we need to construct the path at compile time.&lt;br/&gt;        // This is a limitation for testing, normally you&amp;#39;d use it with a known file.&lt;br/&gt;        // For testing, we&amp;#39;ll manually verify a file known to be in the project.&lt;br/&gt;        // Let&amp;#39;s test `lib.rs` itself for a more realistic scenario.&lt;br/&gt;        let macro_hash = get_file_hash!(&amp;#34;lib.rs&amp;#34;);&lt;br/&gt;&lt;br/&gt;        // We will assert on a known file within the crate.&lt;br/&gt;        let bytes = include_bytes!(&amp;#34;lib.rs&amp;#34;);&lt;br/&gt;        let mut hasher_manual = Sha256::new();&lt;br/&gt;        hasher_manual.update(bytes);&lt;br/&gt;        let expected_hash_lib_rs = hasher_manual.finalize()&lt;br/&gt;            .iter()&lt;br/&gt;            .map(|b| format!(&amp;#34;{:02x}&amp;#34;, b))&lt;br/&gt;            .collect::&amp;lt;String&amp;gt;();&lt;br/&gt;&lt;br/&gt;        assert_eq!(macro_hash, expected_hash_lib_rs);&lt;br/&gt;&lt;br/&gt;        // Test with another known file, e.g., Cargo.toml of the core crate&lt;br/&gt;        let cargo_toml_hash = get_file_hash!(&amp;#34;../Cargo.toml&amp;#34;);&lt;br/&gt;        let cargo_toml_bytes = include_bytes!(&amp;#34;../Cargo.toml&amp;#34;);&lt;br/&gt;        let mut cargo_toml_hasher = Sha256::new();&lt;br/&gt;        cargo_toml_hasher.update(cargo_toml_bytes);&lt;br/&gt;        let expected_cargo_toml_hash = cargo_toml_hasher.finalize()&lt;br/&gt;            .iter()&lt;br/&gt;            .map(|b| format!(&amp;#34;{:02x}&amp;#34;, b))&lt;br/&gt;            .collect::&amp;lt;String&amp;gt;();&lt;br/&gt;        assert_eq!(cargo_toml_hash, expected_cargo_toml_hash);&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[test]&lt;br/&gt;    fn test_get_git_tracked_files() {&lt;br/&gt;        let dir = tempfile::tempdir().unwrap();&lt;br/&gt;        let repo_path = dir.path();&lt;br/&gt;&lt;br/&gt;        // Initialize a git repository&lt;br/&gt;        let _ = Command::new(&amp;#34;git&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;init&amp;#34;)&lt;br/&gt;            .current_dir(repo_path)&lt;br/&gt;            .stdout(std::process::Stdio::null())&lt;br/&gt;            .stderr(std::process::Stdio::null())&lt;br/&gt;            .output()&lt;br/&gt;            .expect(&amp;#34;Failed to initialize git repo&amp;#34;);&lt;br/&gt;&lt;br/&gt;        // Create some files&lt;br/&gt;        let file1_path = repo_path.join(&amp;#34;file1.txt&amp;#34;);&lt;br/&gt;        File::create(&amp;amp;file1_path).unwrap().write_all(b&amp;#34;content1&amp;#34;).unwrap();&lt;br/&gt;        let file2_path = repo_path.join(&amp;#34;file2.txt&amp;#34;);&lt;br/&gt;        File::create(&amp;amp;file2_path).unwrap().write_all(b&amp;#34;content2&amp;#34;).unwrap();&lt;br/&gt;&lt;br/&gt;        // Add and commit files&lt;br/&gt;        let _ = Command::new(&amp;#34;git&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;add&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;.&amp;#34;)&lt;br/&gt;            .current_dir(repo_path)&lt;br/&gt;            .stdout(std::process::Stdio::null())&lt;br/&gt;            .stderr(std::process::Stdio::null())&lt;br/&gt;            .output()&lt;br/&gt;            .expect(&amp;#34;Failed to git add files&amp;#34;);&lt;br/&gt;        let _ = Command::new(&amp;#34;git&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;commit&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;-m&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;Initial commit&amp;#34;)&lt;br/&gt;            .current_dir(repo_path)&lt;br/&gt;            .stdout(std::process::Stdio::null())&lt;br/&gt;            .stderr(std::process::Stdio::null())&lt;br/&gt;            .output()&lt;br/&gt;            .expect(&amp;#34;Failed to git commit&amp;#34;);&lt;br/&gt;&lt;br/&gt;        let tracked_files = get_git_tracked_files(&amp;amp;repo_path.to_path_buf());&lt;br/&gt;        assert_eq!(tracked_files.len(), 2);&lt;br/&gt;        assert!(tracked_files.contains(&amp;amp;&amp;#34;file1.txt&amp;#34;.to_string()));&lt;br/&gt;        assert!(tracked_files.contains(&amp;amp;&amp;#34;file2.txt&amp;#34;.to_string()));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    // #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    // #[test]&lt;br/&gt;    // fn test_file_hash_as_nostr_private_key() {&lt;br/&gt;    //     use super::file_hash_as_nostr_private_key;&lt;br/&gt;    //     // use std::fs::{File, remove_file};&lt;br/&gt;    //     // use std::io::Write;&lt;br/&gt;    //     // use tempfile::tempdir; // Not needed as we&amp;#39;re using a literal path&lt;br/&gt;    //     use nostr_sdk::prelude::ToBech32;&lt;br/&gt;&lt;br/&gt;    //     let file_path = PathBuf::from(&amp;#34;test_nostr_file_for_macro.txt&amp;#34;);&lt;br/&gt;    //     let content = &amp;#34;Nostr test content!&amp;#34;;&lt;br/&gt;    //     File::create(&amp;amp;file_path).unwrap().write_all(content.as_bytes()).unwrap();&lt;br/&gt;&lt;br/&gt;    //     let keys = file_hash_as_nostr_private_key!(&amp;#34;test_nostr_file_for_macro.txt&amp;#34;);&lt;br/&gt;&lt;br/&gt;    //     assert!(!keys.public_key().to_bech32().unwrap().is_empty());&lt;br/&gt;&lt;br/&gt;    //     remove_file(&amp;amp;file_path).unwrap();&lt;br/&gt;    // }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_metadata_event_tr() {&lt;br/&gt;        use super::publish_metadata_event;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let picture_url = super::DEFAULT_PICTURE_URL;&lt;br/&gt;        let banner_url = super::DEFAULT_BANNER_URL;&lt;br/&gt;        let file_path_str = &amp;#34;test_file.txt&amp;#34;;&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the function doesn&amp;#39;t panic&lt;br/&gt;        // and goes through its execution path.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        let relay_urls = super::get_relay_urls();&lt;br/&gt;        publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            picture_url,&lt;br/&gt;            banner_url,&lt;br/&gt;            file_path_str,&lt;br/&gt;        ).await;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    #[serial]&lt;br/&gt;    async fn test_repository_announcement_event_tr() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::{Keys, EventId};&lt;br/&gt;        use std::str::FromStr;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let project_name = &amp;#34;test-nip34-repo&amp;#34;;&lt;br/&gt;        let description = &amp;#34;A test repository for NIP-34 announcements.&amp;#34;;&lt;br/&gt;        let clone_url = &amp;#34;git@example.com:test/test-nip34-repo.git&amp;#34;;&lt;br/&gt;        let _dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;        let _file_for_euc = &amp;#34;Cargo.toml&amp;#34;; // Use a known file in the project, as required by include_bytes!&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_repo_announcement_picture.jpg&amp;#34&#34;&gt;https://example.com/test_repo_announcement_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_repo_announcement_banner.jpg&amp;#34&#34;&gt;https://example.com/test_repo_announcement_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_repository_announcement_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        repository_announcement!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            project_name,&lt;br/&gt;            description,&lt;br/&gt;            clone_url,&lt;br/&gt;            &amp;#34;../Cargo.toml&amp;#34;, // Pass the string literal directly, correcting path for include_bytes!&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;            );&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_patch_event_tr() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-patch&amp;#34;;&lt;br/&gt;        let commit_id = &amp;#34;fedcba9876543210fedcba9876543210fedcba&amp;#34;;&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_patch_picture.jpg&amp;#34&#34;&gt;https://example.com/test_patch_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_patch_banner.jpg&amp;#34&#34;&gt;https://example.com/test_patch_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_patch_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;        publish_patch!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            commit_id,&lt;br/&gt;            &amp;#34;lib.rs&amp;#34;, // Use an existing file for the patch content&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;        );    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_pull_request_event_tr() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-pr&amp;#34;;&lt;br/&gt;        let commit_id = &amp;#34;0123456789abcdef0123456789abcdef01234567&amp;#34;;&lt;br/&gt;        let clone_url = &amp;#34;git@example.com:test/pr-branch.git&amp;#34;;&lt;br/&gt;        let title = Some(&amp;#34;Feat: Implement NIP-34 PR&amp;#34;);&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_pr_picture.jpg&amp;#34&#34;&gt;https://example.com/test_pr_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_pr_banner.jpg&amp;#34&#34;&gt;https://example.com/test_pr_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_pull_request_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        // Test with a title&lt;br/&gt;        publish_pull_request!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            commit_id,&lt;br/&gt;            clone_url,&lt;br/&gt;            Some(title.unwrap()),&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;            );&lt;br/&gt;        // Test without a title&lt;br/&gt;        publish_pull_request!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            commit_id,&lt;br/&gt;            clone_url&lt;br/&gt;        );&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_pr_update_event_tr() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::{Keys, EventId};&lt;br/&gt;        use std::str::FromStr;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-pr-update&amp;#34;;&lt;br/&gt;        let pr_event_id = EventId::from_str(&amp;#34;f6e4d6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9&amp;#34;).unwrap(); // Placeholder EventId&lt;br/&gt;        let updated_commit_id = &amp;#34;z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0&amp;#34;;&lt;br/&gt;        let updated_clone_url = &amp;#34;git@example.com:test/pr-branch-updated.git&amp;#34;;&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_pr_update_picture.jpg&amp;#34&#34;&gt;https://example.com/test_pr_update_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_pr_update_banner.jpg&amp;#34&#34;&gt;https://example.com/test_pr_update_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_pr_update_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        publish_pr_update!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            &amp;amp;pr_event_id, // Pass a reference to pr_event_id&lt;br/&gt;            updated_commit_id,&lt;br/&gt;            updated_clone_url,&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;        );    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_repository_state_event() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-state&amp;#34;;&lt;br/&gt;        let branch_name = &amp;#34;main&amp;#34;;&lt;br/&gt;        let commit_id = &amp;#34;abcde12345abcde12345abcde12345abcde12345&amp;#34;;&lt;br/&gt;        use nostr_sdk::EventId;&lt;br/&gt;        use std::str::FromStr;&lt;br/&gt;        let _dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_repo_state_picture.jpg&amp;#34&#34;&gt;https://example.com/test_repo_state_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_repo_state_banner.jpg&amp;#34&#34;&gt;https://example.com/test_repo_state_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_repository_state_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        publish_repository_state!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            branch_name,&lt;br/&gt;            commit_id&lt;br/&gt;        );    }&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;    // Test for get_file_hash! macro&lt;br/&gt;    #[test]&lt;br/&gt;    fn test_get_file_hash_tr() {&lt;br/&gt;        let dir = tempfile::tempdir().unwrap();&lt;br/&gt;        let file_path = dir.path().join(&amp;#34;test_file.txt&amp;#34;);&lt;br/&gt;        let content = &amp;#34;Hello, world!&amp;#34;;&lt;br/&gt;        File::create(&amp;amp;file_path).unwrap().write_all(content.as_bytes()).unwrap();&lt;br/&gt;&lt;br/&gt;        // The macro expects a string literal, so we need to construct the path at compile time.&lt;br/&gt;        // This is a limitation for testing, normally you&amp;#39;d use it with a known file.&lt;br/&gt;        // For testing, we&amp;#39;ll manually verify a file known to be in the project.&lt;br/&gt;        // Let&amp;#39;s test `lib.rs` itself for a more realistic scenario.&lt;br/&gt;        let macro_hash = get_file_hash!(&amp;#34;lib.rs&amp;#34;);&lt;br/&gt;&lt;br/&gt;        // We will assert on a known file within the crate.&lt;br/&gt;        let bytes = include_bytes!(&amp;#34;lib.rs&amp;#34;);&lt;br/&gt;        let mut hasher_manual = Sha256::new();&lt;br/&gt;        hasher_manual.update(bytes);&lt;br/&gt;        let expected_hash_lib_rs = hasher_manual.finalize()&lt;br/&gt;            .iter()&lt;br/&gt;            .map(|b| format!(&amp;#34;{:02x}&amp;#34;, b))&lt;br/&gt;            .collect::&amp;lt;String&amp;gt;();&lt;br/&gt;&lt;br/&gt;        assert_eq!(macro_hash, expected_hash_lib_rs);&lt;br/&gt;&lt;br/&gt;        // Test with another known file, e.g., Cargo.toml of the core crate&lt;br/&gt;        let cargo_toml_hash = get_file_hash!(&amp;#34;../Cargo.toml&amp;#34;);&lt;br/&gt;        let cargo_toml_bytes = include_bytes!(&amp;#34;../Cargo.toml&amp;#34;);&lt;br/&gt;        let mut cargo_toml_hasher = Sha256::new();&lt;br/&gt;        cargo_toml_hasher.update(cargo_toml_bytes);&lt;br/&gt;        let expected_cargo_toml_hash = cargo_toml_hasher.finalize()&lt;br/&gt;            .iter()&lt;br/&gt;            .map(|b| format!(&amp;#34;{:02x}&amp;#34;, b))&lt;br/&gt;            .collect::&amp;lt;String&amp;gt;();&lt;br/&gt;        assert_eq!(cargo_toml_hash, expected_cargo_toml_hash);&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[test]&lt;br/&gt;    fn test_get_git_tracked_files_tr() {&lt;br/&gt;        let dir = tempfile::tempdir().unwrap();&lt;br/&gt;        let repo_path = dir.path();&lt;br/&gt;&lt;br/&gt;        // Initialize a git repository&lt;br/&gt;        let _ = Command::new(&amp;#34;git&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;init&amp;#34;)&lt;br/&gt;            .current_dir(repo_path)&lt;br/&gt;            .stdout(std::process::Stdio::null())&lt;br/&gt;            .stderr(std::process::Stdio::null())&lt;br/&gt;            .output()&lt;br/&gt;            .expect(&amp;#34;Failed to initialize git repo&amp;#34;);&lt;br/&gt;&lt;br/&gt;        // Create some files&lt;br/&gt;        let file1_path = repo_path.join(&amp;#34;file1.txt&amp;#34;);&lt;br/&gt;        File::create(&amp;amp;file1_path).unwrap().write_all(b&amp;#34;content1&amp;#34;).unwrap();&lt;br/&gt;        let file2_path = repo_path.join(&amp;#34;file2.txt&amp;#34;);&lt;br/&gt;        File::create(&amp;amp;file2_path).unwrap().write_all(b&amp;#34;content2&amp;#34;).unwrap();&lt;br/&gt;&lt;br/&gt;        // Add and commit files&lt;br/&gt;        let _ = Command::new(&amp;#34;git&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;add&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;.&amp;#34;)&lt;br/&gt;            .current_dir(repo_path)&lt;br/&gt;            .stdout(std::process::Stdio::null())&lt;br/&gt;            .stderr(std::process::Stdio::null())&lt;br/&gt;            .output()&lt;br/&gt;            .expect(&amp;#34;Failed to git add files&amp;#34;);&lt;br/&gt;        let _ = Command::new(&amp;#34;git&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;commit&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;-m&amp;#34;)&lt;br/&gt;            .arg(&amp;#34;Initial commit&amp;#34;)&lt;br/&gt;            .current_dir(repo_path)&lt;br/&gt;            .stdout(std::process::Stdio::null())&lt;br/&gt;            .stderr(std::process::Stdio::null())&lt;br/&gt;            .output()&lt;br/&gt;            .expect(&amp;#34;Failed to git commit&amp;#34;);&lt;br/&gt;&lt;br/&gt;        let tracked_files = get_git_tracked_files(&amp;amp;repo_path.to_path_buf());&lt;br/&gt;        assert_eq!(tracked_files.len(), 2);&lt;br/&gt;        assert!(tracked_files.contains(&amp;amp;&amp;#34;file1.txt&amp;#34;.to_string()));&lt;br/&gt;        assert!(tracked_files.contains(&amp;amp;&amp;#34;file2.txt&amp;#34;.to_string()));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    // #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    // #[test]&lt;br/&gt;    // fn test_file_hash_as_nostr_private_key() {&lt;br/&gt;    //     use super::file_hash_as_nostr_private_key;&lt;br/&gt;    //     // use std::fs::{File, remove_file};&lt;br/&gt;    //     // use std::io::Write;&lt;br/&gt;    //     // use tempfile::tempdir; // Not needed as we&amp;#39;re using a literal path&lt;br/&gt;    //     use nostr_sdk::prelude::ToBech32;&lt;br/&gt;&lt;br/&gt;    //     let file_path = PathBuf::from(&amp;#34;test_nostr_file_for_macro.txt&amp;#34;);&lt;br/&gt;    //     let content = &amp;#34;Nostr test content!&amp;#34;;&lt;br/&gt;    //     File::create(&amp;amp;file_path).unwrap().write_all(content.as_bytes()).unwrap();&lt;br/&gt;&lt;br/&gt;    //     let keys = file_hash_as_nostr_private_key!(&amp;#34;test_nostr_file_for_macro.txt&amp;#34;);&lt;br/&gt;&lt;br/&gt;    //     assert!(!keys.public_key().to_bech32().unwrap().is_empty());&lt;br/&gt;&lt;br/&gt;    //     remove_file(&amp;amp;file_path).unwrap();&lt;br/&gt;    // }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_metadata_event() {&lt;br/&gt;        use super::publish_metadata_event;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let picture_url = super::DEFAULT_PICTURE_URL;&lt;br/&gt;        let banner_url = super::DEFAULT_BANNER_URL;&lt;br/&gt;        let file_path_str = &amp;#34;test_file.txt&amp;#34;;&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the function doesn&amp;#39;t panic&lt;br/&gt;        // and goes through its execution path.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        let relay_urls = super::get_relay_urls();&lt;br/&gt;        publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            picture_url,&lt;br/&gt;            banner_url,&lt;br/&gt;            file_path_str,&lt;br/&gt;        ).await;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    #[serial]&lt;br/&gt;    async fn test_repository_announcement_event() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::{Keys, EventId};&lt;br/&gt;        use std::str::FromStr;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let project_name = &amp;#34;test-nip34-repo&amp;#34;;&lt;br/&gt;        let description = &amp;#34;A test repository for NIP-34 announcements.&amp;#34;;&lt;br/&gt;        let clone_url = &amp;#34;git@example.com:test/test-nip34-repo.git&amp;#34;;&lt;br/&gt;        let _dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;        let _file_for_euc = &amp;#34;Cargo.toml&amp;#34;; // Use a known file in your project, as required by include_bytes!&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_repo_announcement_picture.jpg&amp;#34&#34;&gt;https://example.com/test_repo_announcement_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_repo_announcement_banner.jpg&amp;#34&#34;&gt;https://example.com/test_repo_announcement_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_repository_announcement_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        repository_announcement!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            project_name,&lt;br/&gt;            description,&lt;br/&gt;            clone_url,&lt;br/&gt;            &amp;#34;../Cargo.toml&amp;#34;, // Pass the string literal directly, correcting path for include_bytes!&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;            );&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_patch_event() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-patch&amp;#34;;&lt;br/&gt;        let commit_id = &amp;#34;fedcba9876543210fedcba9876543210fedcba&amp;#34;;&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_patch_picture.jpg&amp;#34&#34;&gt;https://example.com/test_patch_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_patch_banner.jpg&amp;#34&#34;&gt;https://example.com/test_patch_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_patch_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;        publish_patch!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            commit_id,&lt;br/&gt;            &amp;#34;lib.rs&amp;#34;, // Use an existing file for the patch content&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;        );    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_pull_request_event() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-pr&amp;#34;;&lt;br/&gt;        let commit_id = &amp;#34;0123456789abcdef0123456789abcdef01234567&amp;#34;;&lt;br/&gt;        let clone_url = &amp;#34;git@example.com:test/pr-branch.git&amp;#34;;&lt;br/&gt;        let title = Some(&amp;#34;Feat: Implement NIP-34 PR&amp;#34;);&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_pr_picture.jpg&amp;#34&#34;&gt;https://example.com/test_pr_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_pr_banner.jpg&amp;#34&#34;&gt;https://example.com/test_pr_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_pull_request_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        // Test with a title&lt;br/&gt;        publish_pull_request!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            commit_id,&lt;br/&gt;            clone_url,&lt;br/&gt;            Some(title.unwrap()),&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;            );&lt;br/&gt;        // Test without a title&lt;br/&gt;        publish_pull_request!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            commit_id,&lt;br/&gt;            clone_url&lt;br/&gt;        );&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_pr_update_event() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::{Keys, EventId};&lt;br/&gt;        use std::str::FromStr;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-pr-update&amp;#34;;&lt;br/&gt;        let pr_event_id = EventId::from_str(&amp;#34;f6e4d6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9&amp;#34;).unwrap(); // Placeholder EventId&lt;br/&gt;        let updated_commit_id = &amp;#34;z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0&amp;#34;;&lt;br/&gt;        let updated_clone_url = &amp;#34;git@example.com:test/pr-branch-updated.git&amp;#34;;&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_pr_update_picture.jpg&amp;#34&#34;&gt;https://example.com/test_pr_update_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_pr_update_banner.jpg&amp;#34&#34;&gt;https://example.com/test_pr_update_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_pr_update_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        publish_pr_update!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            &amp;amp;pr_event_id, // Pass a reference to pr_event_id&lt;br/&gt;            updated_commit_id,&lt;br/&gt;            updated_clone_url,&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;        );    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    #[serial]&lt;br/&gt;    async fn test_publish_repository_state_event_tr() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;        use nostr_sdk::secp256k1::SecretKey as NostrSecretKey;&lt;br/&gt;        &lt;br/&gt;        // 1. Generate FROST keys (1-of-1 for this test to derive a single Nostr key)&lt;br/&gt;        let (shares, _pubkey_package) = super::generate_frost_keys(2, 2).unwrap();&lt;br/&gt;        let signer_id = frost::Identifier::try_from(1 as u16).unwrap();&lt;br/&gt;        let secret_share = shares.get(&amp;amp;signer_id).unwrap();&lt;br/&gt;&lt;br/&gt;        // Convert FROST secret share&amp;#39;s scalar to a Nostr SecretKey&lt;br/&gt;        let frost_secp_secret_key = secret_share.signing_share().to_scalar();&lt;br/&gt;        let nostr_secret_key = NostrSecretKey::from_slice(&amp;amp;frost_secp_secret_key.to_bytes()).unwrap();&lt;br/&gt;        let keys = Keys::new(nostr_secret_key.into());&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-state&amp;#34;;&lt;br/&gt;        let branch_name = &amp;#34;main&amp;#34;;&lt;br/&gt;        let commit_id = &amp;#34;abcde12345abcde12345abcde12345abcde12345&amp;#34;;&lt;br/&gt;        use nostr_sdk::EventId;&lt;br/&gt;        use std::str::FromStr;&lt;br/&gt;        let _dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_repo_state_picture.jpg&amp;#34&#34;&gt;https://example.com/test_repo_state_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_repo_state_banner.jpg&amp;#34&#34;&gt;https://example.com/test_repo_state_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_repository_state_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        publish_repository_state!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            branch_name,&lt;br/&gt;            commit_id&lt;br/&gt;        );    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[tokio::test]&lt;br/&gt;    async fn test_publish_issue_event_tr() {&lt;br/&gt;        use super::get_relay_urls;&lt;br/&gt;        use nostr_sdk::Keys;&lt;br/&gt;        use nostr_sdk::EventId;&lt;br/&gt;        use std::str::FromStr;&lt;br/&gt;&lt;br/&gt;        let keys = Keys::parse(super::DEFAULT_GNOSTR_KEY).expect(&amp;#34;Failed to create Nostr Keys from DEFAULT_GNOSTR_KEY&amp;#34;);&lt;br/&gt;        let relay_urls = get_relay_urls();&lt;br/&gt;        let d_tag = &amp;#34;test-repo-for-issue&amp;#34;;&lt;br/&gt;        let issue_id = &amp;#34;456&amp;#34;;&lt;br/&gt;        let title = &amp;#34;Feature: Implement NIP-34 Issues&amp;#34;;&lt;br/&gt;        let content = &amp;#34;This is a test issue to verify the NIP-34 issue macro implementation.&amp;#34;;&lt;br/&gt;        let dummy_build_manifest_id = EventId::from_str(super::DUMMY_BUILD_MANIFEST_ID_STR).unwrap();&lt;br/&gt;&lt;br/&gt;        // This test primarily checks that the macro and function compile and execute without panicking.&lt;br/&gt;        // Actual publishing success depends on external network conditions.&lt;br/&gt;        super::publish_metadata_event(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_issue_picture.jpg&amp;#34&#34;&gt;https://example.com/test_issue_picture.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;&lt;a href=&#34;https://example.com/test_issue_banner.jpg&amp;#34&#34;&gt;https://example.com/test_issue_banner.jpg&amp;#34&lt;/a&gt;;,&lt;br/&gt;            &amp;#34;test_publish_issue_event_metadata&amp;#34;,&lt;br/&gt;        ).await;&lt;br/&gt;&lt;br/&gt;        publish_issue!(&lt;br/&gt;            &amp;amp;keys,&lt;br/&gt;            &amp;amp;relay_urls,&lt;br/&gt;            d_tag,&lt;br/&gt;            issue_id,&lt;br/&gt;            title,&lt;br/&gt;            content,&lt;br/&gt;            Some(&amp;amp;dummy_build_manifest_id)&lt;br/&gt;        );&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #[cfg(feature = &amp;#34;nostr&amp;#34;)]&lt;br/&gt;    #[test]&lt;br/&gt;    fn test_frost_signature_flow_tr() {&lt;br/&gt;        let max_signers = 3;&lt;br/&gt;        let min_signers = 2;&lt;br/&gt;        let message = b&amp;#34;This is a test message for FROST signing&amp;#34;;&lt;br/&gt;&lt;br/&gt;        // 1. Key Generation&lt;br/&gt;        let (shares, pubkey_package) = super::generate_frost_keys(max_signers, min_signers).unwrap();&lt;br/&gt;&lt;br/&gt;        let mut commitments = BTreeMap::new();&lt;br/&gt;        let mut nonces_map = BTreeMap::new();&lt;br/&gt;        let mut signature_shares_map = BTreeMap::new();&lt;br/&gt;&lt;br/&gt;        // 2. Commitment Phase (simulated for two signers)&lt;br/&gt;        let signer1_id = frost::Identifier::try_from(1 as u16).unwrap();&lt;br/&gt;        let (nonces1, comms1) = super::create_frost_commitment(&amp;amp;shares[&amp;amp;signer1_id]);&lt;br/&gt;        commitments.insert(signer1_id, comms1);&lt;br/&gt;        nonces_map.insert(signer1_id, nonces1);&lt;br/&gt;&lt;br/&gt;        let signer2_id = frost::Identifier::try_from(2 as u16).unwrap();&lt;br/&gt;        let (nonces2, comms2) = super::create_frost_commitment(&amp;amp;shares[&amp;amp;signer2_id]);&lt;br/&gt;        commitments.insert(signer2_id, comms2);&lt;br/&gt;        nonces_map.insert(signer2_id, nonces2);&lt;br/&gt;&lt;br/&gt;        // 3. Signing Package Creation&lt;br/&gt;        let signing_package = super::create_signing_package(commitments, message);&lt;br/&gt;&lt;br/&gt;        // 4. Signature Share Generation&lt;br/&gt;        let share1 = super::generate_signature_share(&amp;amp;signing_package, &amp;amp;nonces_map[&amp;amp;signer1_id], &amp;amp;shares[&amp;amp;signer1_id]).unwrap();&lt;br/&gt;        signature_shares_map.insert(signer1_id, share1);&lt;br/&gt;&lt;br/&gt;        let share2 = super::generate_signature_share(&amp;amp;signing_package, &amp;amp;nonces_map[&amp;amp;signer2_id], &amp;amp;shares[&amp;amp;signer2_id]).unwrap();&lt;br/&gt;        signature_shares_map.insert(signer2_id, share2);&lt;br/&gt;&lt;br/&gt;        // 5. Aggregation&lt;br/&gt;        let group_signature = super::aggregate_signature_shares(&amp;amp;signing_package, &amp;amp;signature_shares_map, &amp;amp;pubkey_package).unwrap();&lt;br/&gt;&lt;br/&gt;        // 6. Verification&lt;br/&gt;        let group_public_key = pubkey_package.verifying_key();&lt;br/&gt;        super::verify_frost_signature(&amp;amp;group_public_key, message, &amp;amp;group_signature).unwrap();&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;
    </content>
    <updated>2026-04-03T02:24:38Z</updated>
  </entry>

</feed>