nostr_ruby 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +10 -8
- data/README.md +286 -161
- data/lib/bech32.rb +84 -0
- data/lib/client.rb +184 -0
- data/lib/context.rb +48 -0
- data/lib/event.rb +188 -0
- data/lib/event_wizard.rb +31 -0
- data/lib/filter.rb +57 -0
- data/lib/key.rb +15 -0
- data/lib/kind.rb +10 -0
- data/lib/message_handler.rb +107 -0
- data/lib/nostr_ruby.rb +13 -273
- data/lib/signer.rb +89 -0
- data/lib/version.rb +3 -0
- data/nostr_ruby-0.2.0.gem +0 -0
- data/nostr_ruby.gemspec +4 -4
- metadata +19 -9
- data/lib/custom_addr.rb +0 -59
- data/lib/nostr_ruby/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d7328ea01464a11df3e360e02656d1cc2d94fbeb4b3e08bac3890f9f30d07d9
|
4
|
+
data.tar.gz: 024f4c4db11d3b11235578e26248847b04995233d6c1547e999dffd36625e850
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f00bbfb3a37da161802217ecd3706ad10f800e39368601a40ce594efcca83357f798a8162c94c169947fe38fe5c31f68344f248b23c201b7d305dd5086628d4
|
7
|
+
data.tar.gz: af802073aa09d07f26df6bb0a3ff028f80a02d844a16072acc5f59c7f96360361a77104e3a1cdd06f2d65f85efe35ee28743d56ec6f306482ac2fdc2f97fece8
|
data/Gemfile.lock
CHANGED
@@ -3,31 +3,33 @@ PATH
|
|
3
3
|
specs:
|
4
4
|
nostr_ruby (0.2.0)
|
5
5
|
base64 (~> 0.1.1)
|
6
|
-
bech32 (~> 1.
|
6
|
+
bech32 (~> 1.4.0)
|
7
7
|
bip-schnorr (~> 0.4.0)
|
8
|
+
faye-websocket (~> 0.11)
|
8
9
|
json (~> 2.6.2)
|
9
10
|
unicode-emoji (~> 3.3.1)
|
10
|
-
websocket-client-simple (~> 0.6.0)
|
11
11
|
|
12
12
|
GEM
|
13
13
|
remote: https://rubygems.org/
|
14
14
|
specs:
|
15
15
|
base64 (0.1.1)
|
16
|
-
bech32 (1.
|
16
|
+
bech32 (1.4.2)
|
17
17
|
thor (>= 1.1.0)
|
18
18
|
bip-schnorr (0.4.0)
|
19
19
|
ecdsa (~> 1.2.0)
|
20
20
|
ecdsa (1.2.0)
|
21
|
-
|
21
|
+
eventmachine (1.2.7)
|
22
|
+
faye-websocket (0.11.3)
|
23
|
+
eventmachine (>= 0.12.0)
|
24
|
+
websocket-driver (>= 0.5.1)
|
22
25
|
json (2.6.3)
|
23
26
|
thor (1.2.1)
|
24
27
|
unicode-emoji (3.3.1)
|
25
28
|
unicode-version (~> 1.0)
|
26
29
|
unicode-version (1.3.0)
|
27
|
-
websocket (
|
28
|
-
|
29
|
-
|
30
|
-
websocket
|
30
|
+
websocket-driver (0.7.6)
|
31
|
+
websocket-extensions (>= 0.1.0)
|
32
|
+
websocket-extensions (0.1.5)
|
31
33
|
|
32
34
|
PLATFORMS
|
33
35
|
arm64-darwin-21
|
data/README.md
CHANGED
@@ -2,212 +2,337 @@
|
|
2
2
|
|
3
3
|
A ruby library to interact with the [Nostr Protocol](https://github.com/nostr-protocol/nostr).
|
4
4
|
|
5
|
-
|
5
|
+
> [!Warning]
|
6
|
+
> This version in work in progress and breaks the v0.2.0 API
|
6
7
|
|
7
|
-
**Note**: this is a first proof of concept version, the API will probably change in the near future.
|
8
8
|
|
9
|
-
|
9
|
+
## Installation
|
10
10
|
|
11
|
-
|
11
|
+
Add this line to your application's Gemfile:
|
12
12
|
|
13
|
+
```ruby
|
14
|
+
# Gemfile
|
15
|
+
gem 'nostr_ruby'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
```shell
|
21
|
+
$ bundle
|
13
22
|
```
|
14
|
-
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
```shell
|
26
|
+
$ gem install nostr_ruby
|
15
27
|
```
|
28
|
+
|
16
29
|
## Usage
|
17
|
-
|
30
|
+
|
31
|
+
### Manage keys
|
32
|
+
|
18
33
|
```ruby
|
19
34
|
require "nostr_ruby"
|
20
35
|
|
21
|
-
|
22
|
-
#
|
36
|
+
sk = Nostr::Key.generate_private_key
|
37
|
+
# => "8090fb3fe26e27d539ee349d70890d338c5e2e8b459e04c8e97658f03d2f9f33"
|
38
|
+
|
39
|
+
pk = Nostr::Key.get_public_key(sk)
|
40
|
+
# => "e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6"
|
41
|
+
```
|
42
|
+
|
43
|
+
### Decode entities
|
23
44
|
|
24
|
-
|
25
|
-
|
45
|
+
```ruby
|
46
|
+
|
47
|
+
puplic_key = Nostr::Bech32.decode(npub)
|
48
|
+
# => {:hrp=>"npub", :data=>"e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6"}
|
49
|
+
|
50
|
+
nprofile_data = Nostr::Bech32.decode("nprofile1qqs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgprpmhxue69uhkgar0dehkutnwdaehgu339e3k7mf06ras84")
|
51
|
+
# => {:hrp=>"nprofile", :data=>{:pubkey=>["7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805"], :relay=>["wss://dtonon.nostr1.com/"]}}
|
26
52
|
|
27
|
-
|
28
|
-
# =>
|
53
|
+
note_data = Nostr::Bech32.decode("note1xzce08egncw3mcm8l8edas6rrhgfj9l5uwwv2hz03zym0m9eg5hsxuyajp")
|
54
|
+
# => {:hrp=>"note", :data=>"30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f"}
|
29
55
|
|
30
|
-
|
31
|
-
# => {:
|
56
|
+
nevent_data = Nostr::Bech32.decode("nevent1qqsrpvvhnu5fu8gaudnlnuk7cdp3m5yezl6w88x9t38c3zdhaju52tcpzpmhxue69uhkztnwdaejumr0dshsz9nhwden5te0vfjhvmewdehhxarjxyhxxmmd9uq3wamnwvaz7tmzd96xxmmfdejhytnnda3kjctv9ulrdeva")
|
57
|
+
# => {:hrp=>"nevent", :data=> {:id=>["30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f"], :relay=>["wss://a.nos.lol/", "wss://bevo.nostr1.com/", "wss://bitcoiner.social/"]}}
|
58
|
+
|
59
|
+
naddr_data = Nostr::Bech32.decode("naddr1qvzqqqr4gupzq77777lz9hvwt86xqrsyf2jn588ewk5aclf8mavr80rhmduy5kq9qqdkc6t5w3kx2ttvdamx2ttxdaez6mr0denj6en0wfkkzaqxq5r99")
|
60
|
+
# => => {:hrp=>"naddr", :data=>{:kind=>[30023], :author=>["7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805"], :identifier=>["little-love-for-long-format"]}}
|
32
61
|
```
|
33
62
|
|
34
|
-
###
|
63
|
+
### Encode entities
|
64
|
+
|
35
65
|
```ruby
|
36
|
-
|
37
|
-
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
66
|
+
|
67
|
+
nsec = Nostr::Bech32.encode_nsec(sk)
|
68
|
+
# => "nsec1szg0k0lzdcna2w0wxjwhpzgdxwx9ut5tgk0qfj8fwev0q0f0nuessml5ur"
|
69
|
+
|
70
|
+
npub = Nostr::Bech32.encode_npub(pk)
|
71
|
+
# => "npub1ul0dn02zulr5lnryvktzhyvm0m7d2a62c6l29tntsxev42w56tnqksrtfu"
|
72
|
+
|
73
|
+
nprofile = Nostr::Bech32.encode_nprofile(
|
74
|
+
pubkey: "7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805",
|
75
|
+
relays: ["wss://dtonon.nostr1.com"],
|
76
|
+
)
|
77
|
+
# => "nprofile1qqs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgpzamhxue69uhkgar0dehkutnwdaehgu339e3k7mg60me8x"
|
78
|
+
|
79
|
+
note = Nostr::Bech32.encode_note("30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f")
|
80
|
+
# => "note1xzce08egncw3mcm8l8edas6rrhgfj9l5uwwv2hz03zym0m9eg5hsxuyajp"
|
81
|
+
|
82
|
+
nevent = Nostr::Bech32.encode_nevent(
|
83
|
+
id: "30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f",
|
84
|
+
relays: ["wss://nos.lol"],
|
85
|
+
)
|
86
|
+
# => "nevent1qqsrpvvhnu5fu8gaudnlnuk7cdp3m5yezl6w88x9t38c3zdhaju52tcpp4mhxue69uhkummn9ekx7mqrqsqqqqqpux7e9q"
|
87
|
+
|
88
|
+
naddr = Nostr::Bech32.encode_naddr(
|
89
|
+
author: "7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805",
|
90
|
+
identifier: "little-love-for-long-format",
|
91
|
+
kind: 30023
|
92
|
+
)
|
93
|
+
# => "naddr1qgs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgrqsqqqa28qqdkc6t5w3kx2ttvdamx2ttxdaez6mr0denj6en0wfkkzaqn2tjdj"
|
45
94
|
```
|
46
95
|
|
47
|
-
###
|
96
|
+
### Initialize Client
|
97
|
+
|
48
98
|
```ruby
|
49
|
-
|
50
|
-
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
99
|
+
require "nostr_ruby"
|
100
|
+
|
101
|
+
# Detailed version
|
102
|
+
s = Nostr::Signer.new(private_key: Nostr::Key.generate_private_key)
|
103
|
+
c = Nostr::Client.new(signer: s)
|
104
|
+
|
105
|
+
# Compact version, under the hood Client creates a Signer
|
106
|
+
c = Nostr::Client.new(private_key: Nostr::Key.generate_private_key)
|
107
|
+
|
108
|
+
c.private_key
|
109
|
+
# => "7402b4b1ee09fb37b64ec2a958f1b7815d904c6dd44227bdef7912ef201af97d"
|
110
|
+
|
111
|
+
c.public_key
|
112
|
+
# => "a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466"
|
113
|
+
|
114
|
+
c.nsec
|
115
|
+
# => "nsec1wsptfv0wp8an0djwc2543udhs9weqnrd63pz00000yfw7gq6l97snckpdq"
|
116
|
+
|
117
|
+
c.npub
|
118
|
+
# => "npub15x0nc94kapta9dnncelw52f5x87pwky42y729a585ut322ja53nq72yrcr"
|
59
119
|
```
|
60
120
|
|
61
|
-
### Create
|
121
|
+
### Create, sign and send an event
|
122
|
+
|
62
123
|
```ruby
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
124
|
+
# Initialize a client, under the hood Client creates a Signer
|
125
|
+
c = Nostr::Client.new(
|
126
|
+
private_key: "7402b4b1ee09fb37b64ec2a958f1b7815d904c6dd44227bdef7912ef201af97d",
|
127
|
+
relay: "wss://nos.lol"
|
128
|
+
)
|
129
|
+
|
130
|
+
# Initialize an event
|
131
|
+
e = Nostr::Event.new(
|
132
|
+
kind: ...,
|
133
|
+
pubkey: ...,
|
134
|
+
created_at: ...,
|
135
|
+
tags: ...,
|
136
|
+
content: ...,
|
137
|
+
pow: ...,
|
138
|
+
delegation: ...,
|
139
|
+
)
|
140
|
+
|
141
|
+
# Sign the event
|
142
|
+
e = c.sign(e)
|
143
|
+
|
144
|
+
# - - - - - - - - - - - - - -
|
145
|
+
# Full async mode
|
146
|
+
|
147
|
+
# Set the open callback
|
148
|
+
c.on :connect do |event|
|
149
|
+
puts 'Publish event...'
|
150
|
+
end
|
151
|
+
|
152
|
+
# Set the response callback
|
153
|
+
c.on :ok do |event|
|
154
|
+
puts "Event id: #{event.id}"
|
155
|
+
puts "Accepted: #{event.success}"
|
156
|
+
puts "Message: #{event.message}"
|
157
|
+
end
|
158
|
+
|
159
|
+
# Connect and send the event
|
160
|
+
c.connect
|
161
|
+
c.publish(e)
|
162
|
+
# Do more things
|
163
|
+
c.close
|
164
|
+
|
165
|
+
# - - - - - - - - - - - - - -
|
166
|
+
# Compact sync mode
|
167
|
+
|
168
|
+
c.connect
|
169
|
+
c.publish_and_wait(e)
|
170
|
+
c.close
|
171
|
+
|
73
172
|
```
|
74
173
|
|
75
|
-
###
|
174
|
+
### Set the profile
|
175
|
+
|
76
176
|
```ruby
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
177
|
+
metadata = {
|
178
|
+
name: "Mr Robot",
|
179
|
+
about: "I walk around the city",
|
180
|
+
picture: "https://upload.wikimedia.org/wikipedia/commons/3/35/Mr_robot_photo.jpg",
|
181
|
+
nip05: "mrrobot@mrrobot.com"
|
182
|
+
}
|
183
|
+
|
184
|
+
e = Nostr::Event.new(
|
185
|
+
kind: Nostr::Kind::METADATA,
|
186
|
+
pubkey: c.public_key,
|
187
|
+
content: metadata.to_json,
|
188
|
+
)
|
189
|
+
```
|
190
|
+
|
191
|
+
### Post a note
|
192
|
+
```ruby
|
193
|
+
e = Nostr::Event.new(
|
194
|
+
kind: Nostr::Kind::SHORT_NOTE,
|
195
|
+
pubkey: c.public_key,
|
196
|
+
content: "Hello Nostr!",
|
197
|
+
)
|
87
198
|
```
|
88
199
|
|
89
200
|
### Share a contact list
|
90
201
|
```ruby
|
91
|
-
contact_list =
|
92
|
-
[
|
202
|
+
contact_list = [
|
203
|
+
["54399b6d8200813bfc53177ad4f13d6ab712b6b23f91aefbf5da45aeb5c96b08", "wss://alicerelay.com/", "alice"],
|
93
204
|
["850708b7099215bf9a1356d242c2354939e9a844c1359d3b5209592a0b420452", "wss://bobrelay.com/nostr", "bob"],
|
94
|
-
["f7f4b0072368460a09138bf3966fb1c59d0bdadfc3aff4e59e6896194594a82a", "ws://carolrelay.com/ws", "carol"]
|
205
|
+
["f7f4b0072368460a09138bf3966fb1c59d0bdadfc3aff4e59e6896194594a82a", "ws://carolrelay.com/ws", "carol"]
|
206
|
+
]
|
207
|
+
|
208
|
+
e = Nostr::Event.new(
|
209
|
+
kind: Nostr::Kind::CONTACT_LIST,
|
210
|
+
pubkey: c.public_key,
|
211
|
+
tags: contact_list.map { |c| ['p'] + c },
|
95
212
|
)
|
96
|
-
# =>
|
97
|
-
# ["EVENT",
|
98
|
-
# {:pubkey=>"d0fbe5e40469bba810ecb9e1b0b6c13370592df161655e81497c2eb69d0d5bef",
|
99
|
-
# :created_at=>1672079733,
|
100
|
-
# :kind=>3,
|
101
|
-
# :tags=>
|
102
|
-
# [["p", "54399b6d8200813bfc53177ad4f13d6ab712b6b23f91aefbf5da45aeb5c96b08", "wss://alicerelay.com/", "alice"],
|
103
|
-
# ["p", "850708b7099215bf9a1356d242c2354939e9a844c1359d3b5209592a0b420452", "wss://bobrelay.com/nostr", "bob"],
|
104
|
-
# ["p", "f7f4b0072368460a09138bf3966fb1c59d0bdadfc3aff4e59e6896194594a82a", "ws://carolrelay.com/ws", "carol"]],
|
105
|
-
# :content=>"",
|
106
|
-
# "id"=>"3cdc1b5fa9d29aaa6b068cfb66cfd95f79784792beaec6cbb2645187b1c632e9",
|
107
|
-
# "sig"=>"e560e0d1a42261900c8ec32bf2d2016b95c3291adb45c7bf82ef94061beb44a45d6a768d9be773ec48ba9f54d05b4505bda0c1f21805e2be681c7436b3d39791"}]
|
108
213
|
```
|
109
214
|
|
110
|
-
###
|
215
|
+
### Delete an event
|
111
216
|
```ruby
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
# :tags=>[["p", "da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81"]],
|
119
|
-
# :content=>"AIZ7vomEJEFgB934gWzlNA==?iv=mwKLb6lZSG5X1y1BNYv6dg==",
|
120
|
-
# "id"=>"6a3efcf47a31bb05aeca0b13bf1f9b9e91b126e0a67e783253fb3bae20f0dc63",
|
121
|
-
# "sig"=>"0e390bb3c783157b3e32c3f6641fb40df9f62e326ac8a3448a70c94103b909e80292a2c4530562298f2c3935899111843a43548185abf09abf583bc3e6e3ddde"}]
|
217
|
+
event_to_delete = "b91b3fb40128112c38dc54168b9f601c22bf8fcae6e70bb2a5f53e7f3ae44388"
|
218
|
+
e = Nostr::Event.new(
|
219
|
+
kind: Nostr::Kind::DELETION,
|
220
|
+
pubkey: c.public_key,
|
221
|
+
tags: [["e", event_to_delete]],
|
222
|
+
)
|
122
223
|
```
|
123
224
|
|
124
|
-
###
|
225
|
+
### React to an event
|
125
226
|
```ruby
|
126
|
-
|
127
|
-
|
128
|
-
|
227
|
+
e = Nostr::Event.new(
|
228
|
+
kind: Nostr::Kind::REACTION,
|
229
|
+
pubkey: c.public_key,
|
230
|
+
content: "+",
|
231
|
+
tags: [["e", target_event]],
|
232
|
+
)
|
129
233
|
|
130
|
-
|
131
|
-
|
234
|
+
# You can also use emoji
|
235
|
+
e2 = Nostr::Event.new(
|
236
|
+
kind: Nostr::Kind::REACTION,
|
237
|
+
pubkey: c.public_key,
|
238
|
+
content: "🔥",
|
239
|
+
tags: [["e", target_event]],
|
240
|
+
)
|
132
241
|
```
|
133
242
|
|
134
|
-
###
|
243
|
+
### Create events with a PoW difficulty
|
135
244
|
```ruby
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
# :content=>"Duplicate",
|
144
|
-
# "id"=>"e4a8556da9dc35da54dff747593073a90ac1de55131ca0deef6a5fd3b402d5fd",
|
145
|
-
# "sig"=>"95ccb5e965c1a6ba36b919a00cd7d3b65286435f93f49a2ebb846dc791a61179e55d544ebf00d4f8eeb53b0a75a97c072287c7458dfbaccb70b4aef6b0acf766"
|
245
|
+
# Just add the `pow` argument
|
246
|
+
e = Nostr::Event.new(
|
247
|
+
kind: Nostr::Kind::SHORT_NOTE,
|
248
|
+
pubkey: c.public_key,
|
249
|
+
content: "Hello Nostr!",
|
250
|
+
pow: 15,
|
251
|
+
)
|
146
252
|
```
|
147
253
|
|
148
|
-
###
|
254
|
+
### Create an event with a NIP-26 delegation
|
255
|
+
```ruby
|
256
|
+
delegator = Nostr::Client.new(private_key: delegator_key)
|
257
|
+
|
258
|
+
delegatee = "b1d8dfd69fe8795042dbbc4d3f85938a01d4740c54d2daf11088c75c50ff19d9"
|
259
|
+
conditions = "kind=1&created_at>#{Time.now.to_i}&created_at<#{(Time.now + 60*60).to_i}"
|
260
|
+
delegation_tag = delegator.generate_delegation_tag(
|
261
|
+
to: delegatee,
|
262
|
+
conditions: conditions
|
263
|
+
)
|
264
|
+
|
265
|
+
# The `delegation_tag` is given to the delegatee so it can use it
|
266
|
+
|
267
|
+
delegatee = Nostr::Client.new(private_key: delegatee_key)
|
268
|
+
|
269
|
+
e = Nostr::Event.new(
|
270
|
+
kind: Nostr::Kind::SHORT_NOTE,
|
271
|
+
pubkey: delegatee.public_key,
|
272
|
+
content: "Hello Nostr!",
|
273
|
+
delegation: delegation_tag,
|
274
|
+
)
|
275
|
+
|
276
|
+
delegatee.sign(e)
|
277
|
+
```
|
278
|
+
### Send a direct message
|
279
|
+
Warning: This uses NIP-04, that will be deprecated in favor of NIP-17
|
149
280
|
```ruby
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
# :content=>"🔥",
|
158
|
-
# "id"=>"f267c8ee24989b633b261efaa3892b07cdc90af80cedfd007b24a5c6232fc631",
|
159
|
-
# "sig"=>"84f2fc213337c6d2c26a4638b1db4e39f788811acd5bce5b9141b7ef56a9aa80768fbbd1109a7783a0d3033732675e231de286c6c745e62436865f4f15b838b6"}]
|
281
|
+
|
282
|
+
e = Nostr::Event.new(
|
283
|
+
kind: Nostr::Kind::DIRECT_MESSAGE,
|
284
|
+
pubkey: c.public_key,
|
285
|
+
tags: [["p", Nostr::Bech32.decode(recipient)[:data]]],
|
286
|
+
content: "Hello Alice!"
|
287
|
+
)
|
160
288
|
```
|
161
289
|
|
162
|
-
###
|
290
|
+
### Decrypt a direct message
|
291
|
+
Warning: This uses NIP-04, that will be deprecated in favor of NIP-17
|
163
292
|
```ruby
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
```
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
#
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
### Reset the NIP-26 delegation
|
210
|
-
```ruby
|
211
|
-
n.reset_delegation
|
212
|
-
#=> nil
|
293
|
+
payload = {
|
294
|
+
:kind=>4,
|
295
|
+
:pubkey=>"a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466",
|
296
|
+
:created_at=>1725387307,
|
297
|
+
:tags=>[["p", "e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6"]],
|
298
|
+
:content=>"Nd7n/wId1oiprUCC4WWwNw==?iv=7gIRExcyO1xystretLIPnQ==",
|
299
|
+
:id=>"b91b3fb40128112c38dc54168b9f601c22bf8fcae6e70bb2a5f53e7f3ae44388",
|
300
|
+
:sig=>"73edf5a6acbefdd3d76f28ba90faaabe348a24c798f8fa33797eec29e2404c33a455815a59472ecd023441df38d815f83d81b95b8cb2f2c88a52982c8f7301e9"
|
301
|
+
}
|
302
|
+
e = Nostr::Event.new(**payload)
|
303
|
+
|
304
|
+
c.decrypt(e)
|
305
|
+
puts e.content
|
306
|
+
=> "Hello Alice!"
|
307
|
+
```
|
308
|
+
|
309
|
+
### Create a subscribtion to receive events
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
filter = Nostr::Filter.new(
|
313
|
+
kinds: [1],
|
314
|
+
authors: ["a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466"],
|
315
|
+
since: Time.now - (60*60*24), # 24 hours ago
|
316
|
+
limit: 10
|
317
|
+
)
|
318
|
+
|
319
|
+
c.on :connect do |message|
|
320
|
+
puts "Connected!"
|
321
|
+
end
|
322
|
+
|
323
|
+
c.on :event do |message|
|
324
|
+
puts ">> #{message.content}"
|
325
|
+
end
|
326
|
+
|
327
|
+
c.on :eose do |message|
|
328
|
+
puts "Finished subscription #{message.subscription_id}"
|
329
|
+
c.close
|
330
|
+
end
|
331
|
+
|
332
|
+
c.on :close do |message|
|
333
|
+
puts "Connection closed"
|
334
|
+
end
|
335
|
+
|
336
|
+
c.subscribe(filter: filter)
|
337
|
+
c.connect
|
213
338
|
```
|
data/lib/bech32.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'bech32'
|
2
|
+
require 'bech32/nostr'
|
3
|
+
require 'bech32/nostr/entity'
|
4
|
+
|
5
|
+
module Nostr
|
6
|
+
class Bech32
|
7
|
+
|
8
|
+
def self.decode(bech32_entity)
|
9
|
+
e = ::Bech32::Nostr::NIP19.decode(bech32_entity)
|
10
|
+
|
11
|
+
case e
|
12
|
+
in ::Bech32::Nostr::BareEntity
|
13
|
+
{ hrp: e.hrp, data: e.data }
|
14
|
+
in ::Bech32::Nostr::TLVEntity
|
15
|
+
{ hrp: e.hrp, data: transform_entries(e.entries) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.encode(hrp, data)
|
20
|
+
::Bech32::Nostr::BareEntity.new(hrp, data).encode
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.encode_npub(data)
|
24
|
+
encode("npub", data)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.encode_nsec(data)
|
28
|
+
encode("nsec", data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.encode_nprofile(pubkey:, relays: [])
|
32
|
+
entry_relays = relays.map do |relay_url|
|
33
|
+
::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_RELAY, relay_url)
|
34
|
+
end
|
35
|
+
|
36
|
+
pubkey_entry = ::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_SPECIAL, pubkey)
|
37
|
+
entries = [pubkey_entry, *entry_relays].compact
|
38
|
+
::Bech32::Nostr::TLVEntity.new(::Bech32::Nostr::NIP19::HRP_PROFILE, entries).encode
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.encode_note(data)
|
42
|
+
encode("note", data)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.encode_nevent(id:, relays: [], kind: nil)
|
46
|
+
entry_relays = relays.map do |r|
|
47
|
+
::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_RELAY, r)
|
48
|
+
end
|
49
|
+
|
50
|
+
entry_id = ::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_SPECIAL, id)
|
51
|
+
entry_kind = kind ? ::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_KIND, kind) : nil
|
52
|
+
|
53
|
+
entries = [entry_id, *entry_relays, entry_kind].compact
|
54
|
+
::Bech32::Nostr::TLVEntity.new(::Bech32::Nostr::NIP19::HRP_EVENT, entries).encode
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.encode_naddr(author:, relays: [], kind: nil, identifier: nil)
|
58
|
+
entry_relays = relays.map do |r|
|
59
|
+
::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_RELAY, r)
|
60
|
+
end
|
61
|
+
|
62
|
+
entry_author = ::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_AUTHOR, author)
|
63
|
+
entry_kind = ::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_KIND, kind)
|
64
|
+
entry_identifier = ::Bech32::Nostr::TLVEntry.new(::Bech32::Nostr::TLVEntity::TYPE_SPECIAL, identifier)
|
65
|
+
|
66
|
+
entries = [entry_author, *entry_relays, entry_kind, entry_identifier].compact
|
67
|
+
::Bech32::Nostr::TLVEntity.new(::Bech32::Nostr::NIP19::HRP_EVENT_COORDINATE, entries).encode
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Helper method to transform entries into a hash
|
73
|
+
def self.transform_entries(entries)
|
74
|
+
entries.each_with_object({}) do |entry, hash|
|
75
|
+
label = entry.instance_variable_get(:@label).to_sym
|
76
|
+
value = entry.instance_variable_get(:@value)
|
77
|
+
|
78
|
+
hash[label] ||= []
|
79
|
+
hash[label] << value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|