Compare commits
5 commits
main
...
nostr-type
Author | SHA1 | Date | |
---|---|---|---|
d75fa95a7d | |||
ca7d2319ea | |||
e19a0f738f | |||
4fed259118 | |||
3a50a04f2d |
14 changed files with 554 additions and 289 deletions
287
Cargo.lock
generated
287
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ab_glyph"
|
||||
|
@ -108,16 +108,6 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
|
@ -180,6 +170,12 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
|
@ -536,7 +532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f"
|
||||
dependencies = [
|
||||
"bitcoin-internals",
|
||||
"bitcoin_hashes 0.14.0",
|
||||
"bitcoin_hashes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -545,18 +541,6 @@ version = "0.21.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bech32"
|
||||
version = "0.11.0"
|
||||
|
@ -595,17 +579,6 @@ dependencies = [
|
|||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bip39"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f"
|
||||
dependencies = [
|
||||
"bitcoin_hashes 0.11.0",
|
||||
"serde",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
|
@ -638,7 +611,7 @@ dependencies = [
|
|||
"bitcoin-internals",
|
||||
"bitcoin-io",
|
||||
"bitcoin-units",
|
||||
"bitcoin_hashes 0.14.0",
|
||||
"bitcoin_hashes",
|
||||
"hex-conservative",
|
||||
"hex_lit",
|
||||
"secp256k1",
|
||||
|
@ -670,12 +643,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin_hashes"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4"
|
||||
|
||||
[[package]]
|
||||
name = "bitcoin_hashes"
|
||||
version = "0.14.0"
|
||||
|
@ -723,15 +690,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-sys"
|
||||
version = "0.1.0-beta.1"
|
||||
|
@ -862,15 +820,6 @@ dependencies = [
|
|||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.95"
|
||||
|
@ -929,38 +878,18 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.9.1"
|
||||
name = "chrono"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20poly1305"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"chacha20",
|
||||
"cipher",
|
||||
"poly1305",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
"zeroize",
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1194,7 +1123,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
|
@ -1244,7 +1172,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1838,10 +1765,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2080,15 +2005,6 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
|
@ -2102,12 +2018,13 @@ dependencies = [
|
|||
name = "hoot-app"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"chrono",
|
||||
"eframe",
|
||||
"egui_extras",
|
||||
"egui_tabs",
|
||||
"ewebsock",
|
||||
"image 0.25.1",
|
||||
"nostr",
|
||||
"nostrdb",
|
||||
"pollster",
|
||||
"puffin",
|
||||
|
@ -2138,6 +2055,29 @@ version = "1.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icrate"
|
||||
version = "0.0.4"
|
||||
|
@ -2227,16 +2167,6 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
|
@ -2244,9 +2174,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2633,18 +2560,6 @@ dependencies = [
|
|||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "negentropy"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe"
|
||||
|
||||
[[package]]
|
||||
name = "negentropy"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a88da9dd148bbcdce323dd6ac47d369b4769d4a3b78c6c52389b9269f77932"
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
|
@ -2685,32 +2600,6 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||
|
||||
[[package]]
|
||||
name = "nostr"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aad4b767bbed24ac5eb4465bfb83bc1210522eb99d67cf4e547ec2ec7e47786"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"bech32",
|
||||
"bip39",
|
||||
"bitcoin",
|
||||
"cbc",
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"getrandom",
|
||||
"instant",
|
||||
"negentropy 0.3.1",
|
||||
"negentropy 0.4.3",
|
||||
"once_cell",
|
||||
"scrypt",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"unicode-normalization",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nostrdb"
|
||||
version = "0.3.4"
|
||||
|
@ -2926,12 +2815,6 @@ version = "1.20.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "orbclient"
|
||||
version = "0.3.47"
|
||||
|
@ -2995,33 +2878,12 @@ dependencies = [
|
|||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
|
@ -3113,17 +2975,6 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
|
@ -3487,7 +3338,7 @@ version = "0.8.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64",
|
||||
"bitflags 2.6.0",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
@ -3584,15 +3435,6 @@ version = "1.0.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "salsa20"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
|
@ -3614,18 +3456,6 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "scrypt"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
|
||||
dependencies = [
|
||||
"password-hash",
|
||||
"pbkdf2",
|
||||
"salsa20",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sctk-adwaita"
|
||||
version = "0.8.1"
|
||||
|
@ -3645,7 +3475,7 @@ version = "0.29.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
|
||||
dependencies = [
|
||||
"bitcoin_hashes 0.14.0",
|
||||
"bitcoin_hashes",
|
||||
"rand",
|
||||
"secp256k1-sys",
|
||||
"serde",
|
||||
|
@ -3715,7 +3545,6 @@ version = "1.0.121"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
|
@ -3753,17 +3582,6 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
|
@ -4371,16 +4189,6 @@ version = "0.2.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
|
@ -4396,7 +4204,6 @@ dependencies = [
|
|||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4405,7 +4212,7 @@ version = "0.37.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64",
|
||||
"log",
|
||||
"pico-args",
|
||||
"usvg-parser",
|
||||
|
|
|
@ -25,10 +25,11 @@ puffin_http = { version = "0.16.0", optional = true }
|
|||
ewebsock = { version = "0.6.0", features = ["tls"] }
|
||||
nostrdb = { git = "https://github.com/damus-io/nostrdb-rs", rev = "ee8afeeb0b6695fca6d27dd0b74a8dc159e37b95" }
|
||||
rand = "0.8.5"
|
||||
nostr = { version = "0.37.0", features = ["std", "nip59"] }
|
||||
serde = "1.0.204"
|
||||
serde_json = "1.0.121"
|
||||
pollster = "0.4.0"
|
||||
bitcoin = { version = "0.32.5", features = ["serde", "rand-std"] }
|
||||
chrono = { version = "0.4.39", features = ["serde"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
security-framework = "3.0.0"
|
||||
|
|
1
rust-toolchain
Normal file
1
rust-toolchain
Normal file
|
@ -0,0 +1 @@
|
|||
1.83.0
|
|
@ -1,8 +1,9 @@
|
|||
use nostr::{Event, EventBuilder, Keys, Kind, PublicKey, Tag, TagKind, TagStandard};
|
||||
use crate::nostr::{Event, EventBuilder, EventKind, Tag};
|
||||
use bitcoin::secp256k1::{PublicKey, Keypair};
|
||||
use std::collections::HashMap;
|
||||
use pollster::FutureExt as _;
|
||||
|
||||
pub const MAIL_EVENT_KIND: u16 = 1059;
|
||||
pub const MAIL_EVENT_KIND: u32 = 1059;
|
||||
|
||||
pub struct MailMessage {
|
||||
pub to: Vec<PublicKey>,
|
||||
|
@ -13,24 +14,28 @@ pub struct MailMessage {
|
|||
}
|
||||
|
||||
impl MailMessage {
|
||||
pub fn to_events(&mut self, sending_keys: &Keys) -> HashMap<PublicKey, Event> {
|
||||
pub fn to_events(&mut self, sending_keys: &Keypair) -> HashMap<PublicKey, Event> {
|
||||
let mut pubkeys_to_send_to: Vec<PublicKey> = Vec::new();
|
||||
let mut tags: Vec<Tag> = Vec::new();
|
||||
|
||||
for pubkey in &self.to {
|
||||
tags.push(Tag::public_key(*pubkey));
|
||||
tags.push(Tag::new_with_values(vec!["p".to_string(), *pubkey.to_hex().as_str()]));
|
||||
pubkeys_to_send_to.push(*pubkey);
|
||||
}
|
||||
|
||||
for pubkey in &self.cc {
|
||||
tags.push(Tag::custom(TagKind::p(), vec![pubkey.to_hex().as_str(), "cc"]));
|
||||
tags.push(Tag::new_with_values(vec!["p".to_string(), *pubkey.to_hex().as_str(), "cc".to_string()]));
|
||||
pubkeys_to_send_to.push(*pubkey);
|
||||
}
|
||||
|
||||
tags.push(Tag::from_standardized(TagStandard::Subject(self.subject.clone())));
|
||||
tags.push(Tag::new_with_values(vec!["subject".to_string(), self.subject.clone()]));
|
||||
|
||||
let base_event = EventBuilder::new(Kind::Custom(MAIL_EVENT_KIND), &self.content)
|
||||
.tags(tags);
|
||||
let base_event = EventBuilder::new()
|
||||
.kind(EventKind::MailEvent)
|
||||
.content(&self.content)
|
||||
.tags(tags)
|
||||
.build();
|
||||
base_event.pubkey = sending_keys.clone().public_key();
|
||||
|
||||
let mut event_list: HashMap<PublicKey, Event> = HashMap::new();
|
||||
for pubkey in pubkeys_to_send_to {
|
||||
|
|
41
src/main.rs
41
src/main.rs
|
@ -12,6 +12,7 @@ mod keystorage;
|
|||
mod mail_event;
|
||||
mod relay;
|
||||
mod ui;
|
||||
mod nostr;
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
let (non_blocking, _guard) = tracing_appender::non_blocking(std::io::stdout()); // add log files in prod one day
|
||||
|
@ -111,7 +112,7 @@ fn update_app(app: &mut Hoot, ctx: &egui::Context) {
|
|||
if app.account_manager.loaded_keys.len() > 0 {
|
||||
let mut gw_sub = relay::Subscription::default();
|
||||
|
||||
let filter = nostr::Filter::new().kind(nostr::Kind::Custom(mail_event::MAIL_EVENT_KIND)).custom_tag(nostr::SingleLetterTag { character: nostr::Alphabet::P, uppercase: false }, app.account_manager.loaded_keys.clone().into_iter().map(|keys| keys.public_key()));
|
||||
let filter = nostr::Filter::new().add_kind(mail_event::MAIL_EVENT_KIND).add_tag("p", app.account_manager.loaded_keys.clone().into_iter().map(|keys| keys.public_key()));
|
||||
gw_sub.filter(filter);
|
||||
|
||||
// TODO: fix error handling
|
||||
|
@ -208,36 +209,6 @@ fn render_app(app: &mut Hoot, ctx: &egui::Context) {
|
|||
.insert(egui::Id::new(rand::random::<u32>()), state);
|
||||
}
|
||||
|
||||
if ui.button("Send Test Event").clicked() {
|
||||
let temp_keys = nostr::Keys::generate();
|
||||
// todo: lmao
|
||||
let new_event = nostr::EventBuilder::text_note("GFY!")
|
||||
.sign_with_keys(&temp_keys)
|
||||
.unwrap();
|
||||
let event_json = crate::relay::ClientMessage::Event { event: new_event };
|
||||
let _ = &app
|
||||
.relays
|
||||
.send(ewebsock::WsMessage::Text(
|
||||
serde_json::to_string(&event_json).unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if ui.button("Get kind 1 notes").clicked() {
|
||||
let mut filter = nostr::types::Filter::new();
|
||||
filter = filter.kind(nostr::Kind::TextNote);
|
||||
let mut sub = crate::relay::Subscription::default();
|
||||
sub.filter(filter);
|
||||
let c_msg = crate::relay::ClientMessage::from(sub);
|
||||
|
||||
let _ = &app
|
||||
.relays
|
||||
.send(ewebsock::WsMessage::Text(
|
||||
serde_json::to_string(&c_msg).unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
ui.label(format!("total events rendered: {}", app.events.len()));
|
||||
|
||||
TableBuilder::new(ui)
|
||||
|
@ -262,7 +233,7 @@ fn render_app(app: &mut Hoot, ctx: &egui::Context) {
|
|||
ui.checkbox(&mut false, "");
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(event.pubkey.to_string());
|
||||
ui.label(event.pubkey.expect("I NEED PUBLIC KEY").to_string());
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.label(event.content.clone());
|
||||
|
@ -273,7 +244,7 @@ fn render_app(app: &mut Hoot, ctx: &egui::Context) {
|
|||
|
||||
if row.response().clicked() {
|
||||
println!("clicked: {}", event.content.clone());
|
||||
app.focused_post = event.id.to_string();
|
||||
app.focused_post = event.id.expect("WE SHOULD HAVE AN EVENT ID").to_string();
|
||||
app.page = Page::Post;
|
||||
}
|
||||
});
|
||||
|
@ -290,14 +261,14 @@ fn render_app(app: &mut Hoot, ctx: &egui::Context) {
|
|||
let gift_wrapped_event = app
|
||||
.events
|
||||
.iter()
|
||||
.find(|&x| x.id.to_string() == app.focused_post)
|
||||
.find(|&x| x.id.expect("there should be id").to_string() == app.focused_post)
|
||||
.expect("event id should be present inside event list");
|
||||
|
||||
let event_to_display = app.account_manager.unwrap_gift_wrap(gift_wrapped_event).expect("we should be able to unwrap an event we recieved");
|
||||
|
||||
ui.heading("View Message");
|
||||
ui.label(format!("Content: {}", event_to_display.rumor.content));
|
||||
ui.label(match &event_to_display.rumor.tags.find(nostr::TagKind::Subject) {
|
||||
ui.label(match &event_to_display.rumor.tags.find(nostr::event::tag::kind::TagKind::Subject) {
|
||||
Some(s) => match s.content() {
|
||||
Some(c) => format!("Subject: {}", c.to_string()),
|
||||
None => "Subject: None".to_string(),
|
||||
|
|
43
src/nostr/event/id.rs
Normal file
43
src/nostr/event/id.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
type Bytes = [u8; 32];
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct EventId {
|
||||
inner: Bytes,
|
||||
}
|
||||
|
||||
impl EventId {
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &Bytes {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_bytes(&self) -> Bytes {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EventId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use bitcoin::hex::DisplayHex;
|
||||
write!(f, "{}", self.inner.to_lower_hex_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for EventId {
|
||||
fn from(s: String) -> Self {
|
||||
let mut inner = [0u8; 32];
|
||||
let bytes = s.as_bytes();
|
||||
let len = bytes.len().min(32);
|
||||
inner[..len].copy_from_slice(&bytes[..len]);
|
||||
EventId { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for EventId {
|
||||
fn from(array: [u8; 32]) -> Self {
|
||||
EventId { inner: array }
|
||||
}
|
||||
}
|
7
src/nostr/event/kind.rs
Normal file
7
src/nostr/event/kind.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum EventKind {
|
||||
ProfileMetadata = 0,
|
||||
MailEvent = 1059,
|
||||
Custom(u64),
|
||||
}
|
191
src/nostr/event/mod.rs
Normal file
191
src/nostr/event/mod.rs
Normal file
|
@ -0,0 +1,191 @@
|
|||
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Keypair, schnorr::Signature, Secp256k1, hashes::{Hash, sha256}};
|
||||
use serde_json::json;
|
||||
|
||||
pub mod tag;
|
||||
pub use tag::{Tag, list::Tags};
|
||||
pub mod id;
|
||||
pub use id::EventId;
|
||||
pub mod kind;
|
||||
pub use kind::EventKind;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum EventBuilderError {
|
||||
MissingFields
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EventBuilder {
|
||||
pub created_at: Option<i64>,
|
||||
pub kind: Option<EventKind>,
|
||||
pub tags: Tags,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl EventBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn gift_wrap(sender_keypair: &Keypair, recipient_pubkey: &PublicKey, event: Event) -> Result<Event, EventBuilderError> {
|
||||
event.pubkey = Some(sender_keypair.clone().public_key());
|
||||
event.id = Some(event.compute_id());
|
||||
}
|
||||
|
||||
pub fn kind(mut self, kind: EventKind) -> Self {
|
||||
self.kind = Some(kind);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn created_at(mut self, created_at: i64) -> Self {
|
||||
self.created_at = Some(created_at);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tag(mut self, tag: Tag) -> Self {
|
||||
self.tags.push(tag);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Extends the current tags.
|
||||
pub fn tags<I>(mut self, tags: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = Tag>,
|
||||
{
|
||||
tags.into_iter().map(|t| self.tags.push(t));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn content(mut self, content: &str) -> Self {
|
||||
self.content = content.to_owned();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Result<Event, EventBuilderError> {
|
||||
if self.created_at.is_none() || self.kind.is_none() {
|
||||
return Err(EventBuilderError::MissingFields);
|
||||
}
|
||||
|
||||
Ok(Event {
|
||||
created_at: self.created_at.unwrap(),
|
||||
kind: self.kind.unwrap().into(),
|
||||
tags: self.tags.clone(),
|
||||
content: self.content.clone(),
|
||||
id: None,
|
||||
pubkey: None,
|
||||
sig: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Event {
|
||||
pub id: Option<EventId>,
|
||||
pub pubkey: Option<PublicKey>,
|
||||
pub created_at: i64,
|
||||
pub kind: u32,
|
||||
pub tags: Tags,
|
||||
pub content: String,
|
||||
pub sig: Option<Signature>,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
/// Verifies the signature of the event
|
||||
pub fn verify(&self) -> bool {
|
||||
let secp = Secp256k1::verification_only();
|
||||
|
||||
let message = Message::from_digest(*self.id.clone().expect("id should be present").as_bytes());
|
||||
|
||||
secp.verify_schnorr(&self.sig.expect("signature should be present"), &message, &self.pubkey.expect("public key should be present").into()).is_ok()
|
||||
}
|
||||
|
||||
pub fn sign_with_seckey(&mut self, seckey: &SecretKey) -> Result<(), String> {
|
||||
let secp = Secp256k1::new();
|
||||
|
||||
let keypair = Keypair::from_secret_key(&secp, seckey);
|
||||
|
||||
self.sign(&keypair)
|
||||
}
|
||||
|
||||
/// Signs the event with the given private key
|
||||
pub fn sign(&mut self, keypair: &Keypair) -> Result<(), String> {
|
||||
let secp = Secp256k1::new();
|
||||
|
||||
if self.pubkey.is_none() {
|
||||
self.pubkey = Some(keypair.public_key());
|
||||
}
|
||||
|
||||
let id = self.compute_id();
|
||||
self.id = Some(id.clone());
|
||||
let message = Message::from_digest(*id.as_bytes());
|
||||
self.sig = Some(secp.sign_schnorr(&message, keypair));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Computes the event ID
|
||||
fn compute_id(&self) -> EventId {
|
||||
let serialized = json!([
|
||||
0,
|
||||
self.pubkey,
|
||||
self.created_at,
|
||||
self.kind,
|
||||
self.tags,
|
||||
self.content
|
||||
]);
|
||||
|
||||
sha256::Hash::hash(serialized.as_str().unwrap().as_bytes()).to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey};
|
||||
|
||||
fn create_test_event() -> Event {
|
||||
Event {
|
||||
id: Some(EventId::default()),
|
||||
pubkey: Some(PublicKey::from_slice(&[0; 33]).unwrap()),
|
||||
created_at: 1234567890,
|
||||
kind: 1,
|
||||
tags: vec![vec!["tag1".to_string(), "value1".to_string()]].into(),
|
||||
content: "Test content".to_string(),
|
||||
sig: Some(Signature::from_slice(&[0; 64]).unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_id() {
|
||||
let event = create_test_event();
|
||||
let id = event.compute_id();
|
||||
assert_ne!(id, EventId::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_and_verify() {
|
||||
let secp = Secp256k1::new();
|
||||
let secret_key = SecretKey::new(&mut rand::thread_rng());
|
||||
let keypair = Keypair::from_secret_key(&secp, &secret_key);
|
||||
|
||||
let mut event = create_test_event();
|
||||
assert!(event.sign(&keypair).is_ok());
|
||||
assert!(event.verify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_with_seckey() {
|
||||
let secret_key = SecretKey::new(&mut rand::thread_rng());
|
||||
let mut event = create_test_event();
|
||||
assert!(event.sign_with_seckey(&secret_key).is_ok());
|
||||
assert!(event.verify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_invalid_signature() {
|
||||
let mut event = create_test_event();
|
||||
event.content = "Modified content".to_string();
|
||||
assert!(!event.verify());
|
||||
}
|
||||
}
|
11
src/nostr/event/tag/kind.rs
Normal file
11
src/nostr/event/tag/kind.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum TagKind {
|
||||
#[serde(rename = "t")]
|
||||
Tag,
|
||||
#[serde(rename = "p")]
|
||||
Pubkey,
|
||||
#[serde(rename = "subject")]
|
||||
Subject,
|
||||
}
|
3
src/nostr/event/tag/list.rs
Normal file
3
src/nostr/event/tag/list.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
use super::Tag;
|
||||
|
||||
pub type Tags = Vec<Tag>;
|
40
src/nostr/event/tag/mod.rs
Normal file
40
src/nostr/event/tag/mod.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
pub mod list;
|
||||
pub mod kind;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Tag(Vec<String>);
|
||||
|
||||
impl Tag {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Tag::new_with_values(Vec::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_with_values(values: Vec<String>) -> Self {
|
||||
Self(values)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(&self) -> &str {
|
||||
&self.0[0]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn content(&self) -> Option<&str> {
|
||||
self.0.get(1).map(|s| s.as_str())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<String>> for Tag {
|
||||
fn from(value: Vec<String>) -> Self {
|
||||
Tag::new_with_values(value)
|
||||
}
|
||||
}
|
183
src/nostr/filter.rs
Normal file
183
src/nostr/filter.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use std::collections::BTreeMap;
|
||||
use chrono::{DateTime, Utc, serde::ts_seconds_option};
|
||||
use super::EventId;
|
||||
use serde::ser::{SerializeMap, Serializer};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
type GenericTags = BTreeMap<String, Vec<String>>;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Filter {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ids: Option<Vec<EventId>>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub authors: Option<Vec<String>>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub kinds: Option<Vec<u32>>,
|
||||
|
||||
#[serde(with = "ts_seconds_option", skip_serializing_if = "Option::is_none")]
|
||||
pub since: Option<DateTime<Utc>>,
|
||||
|
||||
#[serde(with = "ts_seconds_option", skip_serializing_if = "Option::is_none")]
|
||||
pub until: Option<DateTime<Utc>>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub limit: Option<usize>,
|
||||
|
||||
#[serde(flatten, serialize_with = "serialize_generic_tags")]
|
||||
pub generic_tags: GenericTags,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn set_ids(mut self, ids: Vec<EventId>) -> Self {
|
||||
self.ids = Some(ids);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unset_ids(mut self) -> Self {
|
||||
self.ids = None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_id(mut self, id: EventId) -> Self {
|
||||
self.ids.get_or_insert_with(Vec::new).push(id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn remove_id(mut self, id: &EventId) -> Self {
|
||||
if let Some(ids) = &mut self.ids {
|
||||
ids.retain(|x| x != id);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_authors(mut self, authors: Vec<String>) -> Self {
|
||||
self.authors = Some(authors);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unset_authors(mut self) -> Self {
|
||||
self.authors = None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_author(mut self, author: String) -> Self {
|
||||
self.authors.get_or_insert_with(Vec::new).push(author);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn remove_author(mut self, author: &str) -> Self {
|
||||
if let Some(authors) = &mut self.authors {
|
||||
authors.retain(|x| x != author);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_kinds(mut self, kinds: Vec<u32>) -> Self {
|
||||
self.kinds = Some(kinds);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unset_kinds(mut self) -> Self {
|
||||
self.kinds = None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_kind(mut self, kind: u32) -> Self {
|
||||
self.kinds.get_or_insert_with(Vec::new).push(kind);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn remove_kind(mut self, kind: u32) -> Self {
|
||||
if let Some(kinds) = &mut self.kinds {
|
||||
kinds.retain(|&x| x != kind);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_since(mut self, since: DateTime<Utc>) -> Self {
|
||||
self.since = Some(since);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unset_since(mut self) -> Self {
|
||||
self.since = None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_until(mut self, until: DateTime<Utc>) -> Self {
|
||||
self.until = Some(until);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unset_until(mut self) -> Self {
|
||||
self.until = None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_limit(mut self, limit: usize) -> Self {
|
||||
self.limit = Some(limit);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unset_limit(mut self) -> Self {
|
||||
self.limit = None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_tag(mut self, key: &str, value: Vec<String>) -> Self {
|
||||
self.generic_tags.insert(key.to_owned(), value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn remove_tag(mut self, key: &str, value: &str) -> Self {
|
||||
if let Some(values) = self.generic_tags.get_mut(key) {
|
||||
values.retain(|x| x != value);
|
||||
if values.is_empty() {
|
||||
self.generic_tags.remove(key);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clear_tag(mut self, key: &str) -> Self {
|
||||
self.generic_tags.remove(key);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clear_all_tags(mut self) -> Self {
|
||||
self.generic_tags.clear();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Filter {
|
||||
fn default() -> Self {
|
||||
Filter {
|
||||
ids: None,
|
||||
authors: None,
|
||||
kinds: None,
|
||||
since: None,
|
||||
until: None,
|
||||
limit: None,
|
||||
generic_tags: GenericTags::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_generic_tags<S>(tags: &GenericTags, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_map(Some(tags.len()))?;
|
||||
for (key, value) in tags {
|
||||
map.serialize_entry(key, value)?;
|
||||
}
|
||||
map.end()
|
||||
}
|
4
src/nostr/mod.rs
Normal file
4
src/nostr/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod event;
|
||||
pub use event::{Event, EventBuilder, EventId, Tag, Tags, EventKind};
|
||||
pub mod filter;
|
||||
pub use filter::Filter;
|
|
@ -1,10 +1,8 @@
|
|||
use ewebsock::{WsMessage, WsEvent};
|
||||
use nostr::types::Filter;
|
||||
use nostr::Event;
|
||||
use serde::de::{SeqAccess, Visitor};
|
||||
use crate::nostr::Filter;
|
||||
use crate::nostr::Event;
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt::{self};
|
||||
use serde::{Serialize, Serializer};
|
||||
use crate::error;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
|
|
Loading…
Add table
Reference in a new issue