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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19e9c9f2d37d02877b0f4cdc83faeb46d09d707ee7b54d4ec938ee448fefc5bb
4
- data.tar.gz: c01abc356c5bab165b6534eef10163c40be8c73632c2ce8b657c3ceb3e0cd1c3
3
+ metadata.gz: 9d7328ea01464a11df3e360e02656d1cc2d94fbeb4b3e08bac3890f9f30d07d9
4
+ data.tar.gz: 024f4c4db11d3b11235578e26248847b04995233d6c1547e999dffd36625e850
5
5
  SHA512:
6
- metadata.gz: 2021a0b34822aeb9eaa071fa96a6c48b8c3eb982eabbb2646daae076ddfc39957383dad49ecbde208e129d81af7a0489031afdd91ddfb8a69a011bba88178baa
7
- data.tar.gz: 1959f99e9d18ca5fbe7fb0f5c41943f1981a980716c4871d94d9e23eb7d5c1e39cd0d0665f1bf51d2e21bd9f7b41c7630f7b15fcab583fa336cb4d0ab7d54adc
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.3.0)
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.3.0)
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
- event_emitter (0.2.6)
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 (1.2.9)
28
- websocket-client-simple (0.6.0)
29
- event_emitter
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
- ## Installation
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
- gem install nostr_ruby
23
+
24
+ Or install it yourself as:
25
+ ```shell
26
+ $ gem install nostr_ruby
15
27
  ```
28
+
16
29
  ## Usage
17
- ### Manage the keys
30
+
31
+ ### Manage keys
32
+
18
33
  ```ruby
19
34
  require "nostr_ruby"
20
35
 
21
- n = Nostr.new({private_key: "964b29795d621cdacf05fd94fb23206c88742db1fa50b34d7545f3a2221d8124"})
22
- # <Nostr:0x00000001063ffa28 @private_key="964b29795d621cdacf05fd94fb23206c88742db1fa50b34d7545f3a2221d8124" @public_key="da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81">
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
- n.bech32_keys
25
- # => {:public_key=>"npub1mg2nzunrsk9df94zr3uudhzltnu6lzq2muax09xmhu5gxxrvnkqsvpjg3p", :private_key=>"nsec1je9jj72avgwd4nc9lk20kgeqdjy8gtd3lfgtxnt4ghe6ygsasyjq7kh6c4"}
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
- n = Nostr.new({private_key: "nsec1je9jj72avgwd4nc9lk20kgeqdjy8gtd3lfgtxnt4ghe6ygsasyjq7kh6c4"})
28
- # => #<Nostr:0x00000001060952c0 @private_key="964b29795d621cdacf05fd94fb23206c88742db1fa50b34d7545f3a2221d8124", @public_key="da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81">
53
+ note_data = Nostr::Bech32.decode("note1xzce08egncw3mcm8l8edas6rrhgfj9l5uwwv2hz03zym0m9eg5hsxuyajp")
54
+ # => {:hrp=>"note", :data=>"30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f"}
29
55
 
30
- n.keys
31
- # => {:public_key=>"da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81", :private_key=>"964b29795d621cdacf05fd94fb23206c88742db1fa50b34d7545f3a2221d8124"}
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
- ### Set the user metadata
63
+ ### Encode entities
64
+
35
65
  ```ruby
36
- metadata = n.build_metadata_event("Mr Robot", "I walk around the city", "https://upload.wikimedia.org/wikipedia/commons/3/35/Mr_robot_photo.jpg", "mrrobot@mrrobot.com")
37
- #["EVENT",
38
- # {:pubkey=>"9be59510fa12b77340bb57e555bac716455fedf46d1a354185d4e72bd0340b6f",
39
- # :created_at=>1671546067,
40
- # :kind=>0,
41
- # :tags=>[],
42
- # :content=>"{\"name\":\"Mr Robot\",\"about\":\"I walk around the city\",\"picture\":\"https://upload.wikimedia.org/wikipedia/commons/3/35/Mr_robot_photo.jpg\",\"nip05\":\"mrrobot@mrrobot.com\"}",
43
- # "id"=>"3bd77596ea999dde26689c24370dc4adfa66c33abf1b4c23bf863a516106cda2",
44
- # "sig"=>"2ff752e9f3ed824e7677c41c73728315f0532f3437857774d7a50a577563f391785afd1f84bef3e3574939b14cf096380d4790375953c793504ffcf2f0467d69"}]
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
- ### Create a post
96
+ ### Initialize Client
97
+
48
98
  ```ruby
49
- note = n.build_note_event("Hello Nostr!")
50
- # =>
51
- # ["EVENT",
52
- # {:pubkey=>"da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81",
53
- # :created_at=>1671406583,
54
- # :kind=>1,
55
- # :tags=>[],
56
- # :content=>"Hello Nostr!",
57
- # "id"=>"23411895658d374ec922adf774a70172290b2c738ae67815bd8945e5d8fff3bb",
58
- # "sig"=>"871177b77840bdf092dabacf98c47690647fd6ceb3cc79dd7af7e98c6aded0b808abd5566e2864bd438364cea2f17bd6f9d55091b3c5136839cf160beca42b63"}]
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 a channel post
121
+ ### Create, sign and send an event
122
+
62
123
  ```ruby
63
- channel_note = n.build_note_event("Welcome on my channel :)", "136b0b99eff742e0939799417d04d8b48049672beb6d8110ce6b0fc978cd67a1")
64
- # =>
65
- # ["EVENT",
66
- # {:pubkey=>"da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81",
67
- # :created_at=>1671406522,
68
- # :kind=>42,
69
- # :tags=>[["e", "136b0b99eff742e0939799417d04d8b48049672beb6d8110ce6b0fc978cd67a1"]],
70
- # :content=>"Welcome on my channel :)",
71
- # "id"=>"96ac317516e9cc3bae8238cf11a95a2f12d1bd2f6553c0867d47f3165ca3483b",
72
- # "sig"=>"ccb6cbfa5c3cfac7b7f48dd9cda25d6a2493cbf8df91fa8f9fee2559a20c92613326a319f5b76aff9fef85278e04ce0ee78e636afb4ef2bb000ee8a6fdf418d2"}]
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
- ### Recommend a relay
174
+ ### Set the profile
175
+
76
176
  ```ruby
77
- recommendation = n.build_recommended_relay_event("wss://relay.damus.io")
78
- # =>
79
- # ["EVENT",
80
- # {:pubkey=>"d0fbe5e40469bba810ecb9e1b0b6c13370592df161655e81497c2eb69d0d5bef",
81
- # :created_at=>1672079256,
82
- # :kind=>2,
83
- # :tags=>[],
84
- # :content=>"wss://relay.damus.io",
85
- # "id"=>"1842c9feb3bf2ad7095c8a51238f598fa028116d4fd919af22ad2c63ba3b7d69",
86
- # "sig"=>"9c2f158f379b2d234fd0d363b46a7f90c25392f9296111b6cc04224df8aec69817fa62d7225c12b90fdc31eb89c7afaa427b18147cc8ad6cd411b47dda1331b6"}]
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 = n.build_contact_list_event(
92
- [["54399b6d8200813bfc53177ad4f13d6ab712b6b23f91aefbf5da45aeb5c96b08", "wss://alicerelay.com/", "alice"],
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
- ### Create a private message
215
+ ### Delete an event
111
216
  ```ruby
112
- private_message = n.build_dm_event("Hello!", "da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81") # To myself
113
- # =>
114
- # ["EVENT",
115
- # {:pubkey=>"da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81",
116
- # :created_at=>1671406025,
117
- # :kind=>4,
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
- ### Decrypt a private message
225
+ ### React to an event
125
226
  ```ruby
126
- # Get the reply from the relay
127
- reply
128
- # => ["EVENT","0.27631406274260906",{"tags":[["p","da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81"]],"content":"vWATHf2l/KI7RSyVlbxpvufZc+Lui/0oDysTyfEG5vs=?iv=G4SG7ArMGkglX0UJbJBDUA==","sig":"fe816b86579f5d13ab23c88410364442a9b4393ac0d74f4642cd51a1887f04908ff57ef60409d529a6939c50d77b048320417005460b0353c8f990d1b35c3661","id":"5a0274f33cdb064136c1423ac4b096d7f1e3fb36f60404ef2449ee44331de03f","kind":4,"pubkey":"da15317263858ad496a21c79c6dc5f5cf9af880adf3a6794dbbf2883186c9d81","created_at":1671407520}]
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
- n.decrypt_dm(reply)
131
- # => "Nice to meet you!"
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
- ### Delete an event
243
+ ### Create events with a PoW difficulty
135
244
  ```ruby
136
- deletion = n.build_deletion_event(["23411895658d374ec922adf774a70172290b2c738ae67815bd8945e5d8fff3bb"], "Duplicate")
137
- # =>
138
- # ["EVENT",
139
- # {:pubkey=>"d0fbe5e40469bba810ecb9e1b0b6c13370592df161655e81497c2eb69d0d5bef",
140
- # :created_at=>1672080450,
141
- # :kind=>5,
142
- # :tags=>[["e", "23411895658d374ec922adf774a70172290b2c738ae67815bd8945e5d8fff3bb"]],
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
- ### React to an event
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
- reaction = n.build_reaction_event("🔥", "23411895658d374ec922adf774a70172290b2c738ae67815bd8945e5d8fff3bb", "d0fbe5e40469bba810ecb9e1b0b6c13370592df161655e81497c2eb69d0d5bef")
151
- # =>
152
- # ["EVENT",
153
- # {:pubkey=>"d0fbe5e40469bba810ecb9e1b0b6c13370592df161655e81497c2eb69d0d5bef",
154
- # :created_at=>1672080671,
155
- # :kind=>7,
156
- # :tags=>[["e", "23411895658d374ec922adf774a70172290b2c738ae67815bd8945e5d8fff3bb"], ["p", "d0fbe5e40469bba810ecb9e1b0b6c13370592df161655e81497c2eb69d0d5bef"]],
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
- ### Create events with a PoW difficulty
290
+ ### Decrypt a direct message
291
+ Warning: This uses NIP-04, that will be deprecated in favor of NIP-17
163
292
  ```ruby
164
- n.set_pow_difficulty(16)
165
- note = n.build_note_event("Hello Nostr!")
166
- # =>
167
- # ["EVENT",
168
- # {:pubkey=>"d0fbe5e40469bba810ecb9e1b0b6c13370592df161655e81497c2eb69d0d5bef",
169
- # :created_at=>1672095162,
170
- # :kind=>42,
171
- # :tags=>[["e", "d0fbe5e40469bba810ecb9e1b0b6c13370592df161655e81497c2eb69d0d5bef"], ["nonce", "232735", "16"]],
172
- # :content=>"Hello Nostr!",
173
- # "id"=>"0000fb0c4563274e742e56d7d6de08684a2a25dfb52b79cccdb49c649dccbf45",
174
- # "sig"=>"838a1457c75084319e4723fbd9cbcf4c3311c466daf3908ffa114682094140e3b188996a73ae9fd3d3c6dbf08beecf9081b8d2bf0e60163b07cdf36a50dea1c0"}]
175
- ```
176
-
177
- ### Create events with a NIP-26 delegation
178
- ```ruby
179
- from = Time.now.to_i
180
- to = (Time.now + 60*60).to_i
181
- delegatee_pubkey = "b1d8dfd69fe8795042dbbc4d3f85938a01d4740c54d2daf11088c75c50ff19d9"
182
- conditions = "kind=1&created_at>#{from}&created_at<#{to}"
183
- tag = n.get_delegation_tag(delegatee_pubkey, conditions)
184
- n.set_delegation(tag)
185
- n.build_note_event("I delegate someone to post this!")
186
- #=>
187
- #["EVENT",
188
- # {:pubkey=>"1ed41a3ce33edfc580102abfbdc01d922f8c7697beee3e395aa7dcd7115a3372",
189
- # :created_at=>1681503318,
190
- # :kind=>1,
191
- # :tags=>
192
- # [["delegation",
193
- # "1ed41a3ce33edfc580102abfbdc01d922f8c7697beee3e395aa7dcd7115a3372",
194
- # "kind=1&created_at>1681503305&created_at<1681506905",
195
- # "dbbdc7074a5c2d2a53ee174c43afb0bb03106ced866e0dfa7996fe2553a54aaa9af6affe915ec2e688e26357681bfcf0445d607ed85eca2217f79fb51094d816"]],
196
- # :content=>"I delegate someone to post this!",
197
- # "id"=>"f6fd20535db6748e0529052310c47dc788616b03f8cef20260ca6a2dfb5dedaf",
198
- # "sig"=>"826d7bd1f369f6ab8330746c236437610f13205809d12bc94af287b7ceec180443a3a750267d5152323329a1b2d02d0702364fa3ee8228bab57016b39975e449"}]
199
- ```
200
-
201
- ### Verify a NIP-26 delegation
202
- ```ruby
203
- delegatee_pubkey = "b1d8dfd69fe8795042dbbc4d3f85938a01d4740c54d2daf11088c75c50ff19d9"
204
- tag = ["delegation", delegator_pubkey, conditions, signature]
205
- Nostr.verify_delegation_signature(delegatee_pubkey, tag)
206
- #=> true
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