nostr 0.1.0 → 0.2.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: 669ffcd3d585116e340888ace801690a0c596bb6d20f12acc80a7a8c933fa0e1
4
- data.tar.gz: 1abe2ac66bcdd51c175e2ebb7b676d1d8a3508c12e783e674252d33948336215
3
+ metadata.gz: 90770e269f0c966f76013797314b9e3b4b0743fa57f5b84f2460371b413bc5f1
4
+ data.tar.gz: 7a4a908a6c1b086781d26dc6f45c289a85859e12e1e854451a0e9f8e75f5fa06
5
5
  SHA512:
6
- metadata.gz: fb8845670cf90e66204225260aa76d50bcf76f3e8cd9ccae27bbb73e3384bb23fc411fdabcdbe6a2cbdfd1e5023d45ce5c4f8b42077b73c3c1e865e87fa5cce5
7
- data.tar.gz: 5e02009ec661e34507a96ddb3c1866e76d3aab0a2f19c80f4228a53c783e01a1b226e76d39a35ef8de27b2be9c7a915435ab55666b7770d98cc54cdf1f387478
6
+ metadata.gz: 35a4401f078587bed79d6063f4051cca3f8dc170c074c552a202fee7872b75668eb8204a5f6db09f810ed0c5bb84026438d7e942f48a4825d63ba64552d032c7
7
+ data.tar.gz: b9400efc3228dea17e23e40803e2e7b94ce9966e6a729fcf5ea0b79ffcea07b7e51f7fd9703d01af1d287ffdaf85b6b8d49d325813b8e32518a0be240537e86e
data/.rubocop.yml CHANGED
@@ -1,3 +1,5 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  require:
2
4
  - rubocop-rake
3
5
  - rubocop-rspec
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,23 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2023-01-12 10:00:10 UTC using RuboCop version 1.42.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ # Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
11
+ Metrics/AbcSize:
12
+ Max: 23
13
+
14
+ # Offense count: 2
15
+ # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
16
+ Metrics/MethodLength:
17
+ Max: 16
18
+
19
+ # Offense count: 4
20
+ # Configuration parameters: AssignmentOnly.
21
+ RSpec/InstanceVariable:
22
+ Exclude:
23
+ - 'spec/nostr/client_spec.rb'
data/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.2.0] - 2023-01-12
8
+
9
+ ### Added
10
+
11
+ - [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md) compliant client
12
+
7
13
  ## [0.1.0] - 2023-01-06
8
14
 
9
15
  - Initial release
16
+
17
+ [0.2.0]: https://github.com/wilsonsilva/nostr/compare/v0.1.0...v0.2.0
18
+ [0.1.0]: https://github.com/wilsonsilva/nostr/compare/7fded5...v0.1.0
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # Nostr
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/nostr.svg)](https://badge.fury.io/rb/nostr)
4
- [![Security](https://hakiri.io/github/wilsonsilva/nostr/master.svg)](https://hakiri.io/github/wilsonsilva/nostr/master)
5
- [![Inline docs](http://inch-ci.org/github/wilsonsilva/nostr.svg?branch=master)](http://inch-ci.org/github/wilsonsilva/nostr)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/c7633eb2c89eb95ee7f2/maintainability)](https://codeclimate.com/github/wilsonsilva/nostr/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/c7633eb2c89eb95ee7f2/test_coverage)](https://codeclimate.com/github/wilsonsilva/nostr/test_coverage)
6
6
 
7
- Nostr client. Please note that the API is likely to change as the gem is still in development and has not yet reached a
8
- stable release. Use with caution.
7
+ Asynchronous Nostr client. Please note that the API is likely to change as the gem is still in development and
8
+ has not yet reached a stable release. Use with caution.
9
9
 
10
10
  ## Installation
11
11
 
@@ -19,10 +19,171 @@ If bundler is not being used to manage dependencies, install the gem by executin
19
19
 
20
20
  ## Usage
21
21
 
22
+ ### Requiring the gem
23
+
24
+ All examples below assume that the gem has been required.
25
+
22
26
  ```ruby
23
27
  require 'nostr'
24
28
  ```
25
29
 
30
+
31
+ ### Generating a keypair
32
+
33
+ ```ruby
34
+ keygen = Nostr::Keygen.new
35
+ keypair = keygen.generate_keypair
36
+
37
+ keypair.private_key
38
+ keypair.public_key
39
+ ```
40
+
41
+ ### Generating a private key and a public key
42
+
43
+ ```ruby
44
+ keygen = Nostr::Keygen.new
45
+
46
+ private_key = keygen.generate_private_key
47
+ public_key = keygen.extract_public_key(private_key)
48
+ ```
49
+
50
+ ### Connecting to a Relay
51
+
52
+ Clients can connect to multiple Relays. In this version, a Client can only connect to a single Relay at a time.
53
+
54
+ You may instantiate multiple Clients and multiple Relays.
55
+
56
+ ```ruby
57
+ client = Nostr::Client.new
58
+ relay = Nostr::Relay.new(url: 'wss://relay.damus.io', name: 'Damus')
59
+
60
+ client.connect(relay)
61
+ ```
62
+
63
+ ### WebSocket events
64
+
65
+ All communication between clients and relays happen in WebSockets.
66
+
67
+ The `:connect` event is fired when a connection with a WebSocket is opened. You must call `Nostr::Client#connect` first.
68
+
69
+ ```ruby
70
+ client.on :connect do
71
+ # all the code goes here
72
+ end
73
+ ```
74
+
75
+ The `:close` event is fired when a connection with a WebSocket has been closed because of an error.
76
+
77
+ ```ruby
78
+ client.on :error do |error_message|
79
+ puts error_message
80
+ end
81
+
82
+ # > Network error: wss://rsslay.fiatjaf.com: Unable to verify the server certificate for 'rsslay.fiatjaf.com'
83
+ ```
84
+
85
+ The `:message` event is fired whenwhen data is received through a WebSocket.
86
+
87
+ ```ruby
88
+ client.on :message do |message|
89
+ puts message
90
+ end
91
+
92
+ # > Network error: wss://rsslay.fiatjaf.com: Unable to verify the server certificate for 'rsslay.fiatjaf.com'
93
+ ```
94
+
95
+ The `:close` event is fired when a connection with a WebSocket is closed.
96
+
97
+ ```ruby
98
+ client.on :close do |code, reason|
99
+ # you may attempt to reconnect
100
+
101
+ client.connect(relay)
102
+ end
103
+ ```
104
+
105
+ ### Requesting for events / creating a subscription
106
+
107
+ A client can request events and subscribe to new updates after it has established a connection with the Relay.
108
+
109
+ You may use a `Nostr::Filter` instance with as many attributes as you wish:
110
+
111
+ ```ruby
112
+ client.on :connect do
113
+ filter = Nostr::Filter.new(
114
+ ids: ['8535d5e2d7b9dc07567f676fbe70428133c9884857e1915f5b1cc6514c2fdff8'],
115
+ authors: ['ae00f88a885ce76afad5cbb2459ef0dcf0df0907adc6e4dac16e1bfbd7074577'],
116
+ kinds: [Nostr::EventKind::TEXT_NOTE],
117
+ e: ["f111593a72cc52a7f0978de5ecf29b4653d0cf539f1fa50d2168fc1dc8280e52"],
118
+ p: ["f1f9b0996d4ff1bf75e79e4cc8577c89eb633e68415c7faf74cf17a07bf80bd8"],
119
+ since: 1230981305,
120
+ until: 1292190341,
121
+ limit: 420,
122
+ )
123
+
124
+ subscription = client.subscribe('a_random_subscription_id', filter)
125
+ end
126
+ ```
127
+
128
+ With just a few:
129
+
130
+ ```ruby
131
+ client.on :connect do
132
+ filter = Nostr::Filter.new(kinds: [Nostr::EventKind::TEXT_NOTE])
133
+ subscription = client.subscribe('a_random_subscription_id', filter)
134
+ end
135
+ ```
136
+
137
+ Or omit the filter:
138
+
139
+ ```ruby
140
+ client.on :connect do
141
+ subscription = client.subscribe('a_random_subscription_id')
142
+ end
143
+ ```
144
+
145
+ Or even omit the subscription id:
146
+
147
+ ```ruby
148
+ client.on :connect do
149
+ subscription = client.subscribe('a_random_subscription_id')
150
+ end
151
+ ```
152
+
153
+ ### Stop previous subscriptions
154
+
155
+ You can stop receiving messages from a subscription by calling `#unsubscribe`:
156
+
157
+ ```ruby
158
+ client.unsubscribe('your_subscription_id')
159
+ ```
160
+
161
+ ### Publishing an event
162
+
163
+ To publish an event you need a keypair.
164
+
165
+ ```ruby
166
+ # Set up the private key
167
+ private_key = 'a630b06e2f883378d0aa335b9adaf7734603e00433350b684fe53e184f08c58f'
168
+ user = Nostr::User.new(private_key)
169
+
170
+ # Create a signed event
171
+ event = user.create_event(
172
+ created_at: 1667422587, # optional, defaults to the current time
173
+ kind: Nostr::EventKind::TEXT_NOTE,
174
+ tags: [], # optional, defaults to []
175
+ content: 'Your feedback is appreciated, now pay $8'
176
+ )
177
+
178
+ # Send it to the Relay
179
+ client.publish(event)
180
+ ```
181
+
182
+ ## NIPS
183
+
184
+ - [x] [NIP-01 - Client](https://github.com/nostr-protocol/nips/blob/master/01.md)
185
+ - [ ] [NIP-01 - Relay](https://github.com/nostr-protocol/nips/blob/master/01.md)
186
+
26
187
  ## Development
27
188
 
28
189
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'event_emitter'
4
+ require 'faye/websocket'
5
+
6
+ module Nostr
7
+ # Clients can talk with relays and can subscribe to any set of events using a subscription filters.
8
+ # The filter represents all the set of nostr events that a client is interested in.
9
+ #
10
+ # There is no sign-up or account creation for a client. Every time a client connects to a relay, it submits its
11
+ # subscription filters and the relay streams the "interested events" to the client as long as they are connected.
12
+ #
13
+ class Client
14
+ include EventEmitter
15
+
16
+ # Instantiates a new Client
17
+ #
18
+ # @api public
19
+ #
20
+ # @example Instantiating a client that logs all the events it sends and receives
21
+ # client = Nostr::Client.new(debug: true)
22
+ #
23
+ def initialize
24
+ @subscriptions = {}
25
+
26
+ initialize_channels
27
+ end
28
+
29
+ # Connects to the Relay's websocket endpoint
30
+ #
31
+ # @api public
32
+ #
33
+ # @example Connecting to a relay
34
+ # relay = Nostr::Relay.new(url: 'wss://relay.damus.io', name: 'Damus')
35
+ # client.connect(relay)
36
+ #
37
+ # @param [Relay] relay The relay to connect to
38
+ #
39
+ # @return [void]
40
+ #
41
+ def connect(relay)
42
+ execute_within_an_em_thread do
43
+ client = Faye::WebSocket::Client.new(relay.url, [], { tls: { verify_peer: false } })
44
+ parent_to_child_channel.subscribe { |msg| client.send(msg) }
45
+
46
+ client.on :open do
47
+ child_to_parent_channel.push(type: :open)
48
+ end
49
+
50
+ client.on :message do |event|
51
+ child_to_parent_channel.push(type: :message, data: event.data)
52
+ end
53
+
54
+ client.on :error do |event|
55
+ child_to_parent_channel.push(type: :error, message: event.message)
56
+ end
57
+
58
+ client.on :close do |event|
59
+ child_to_parent_channel.push(type: :close, code: event.code, reason: event.reason)
60
+ end
61
+ end
62
+ end
63
+
64
+ # Subscribes to a set of events using a filter
65
+ #
66
+ # @api public
67
+ #
68
+ # @example Creating a subscription with no id and no filters
69
+ # subscription = client.subscribe
70
+ #
71
+ # @example Creating a subscription with an ID
72
+ # subscription = client.subscribe(subscription_id: 'my-subscription')
73
+ #
74
+ # @example Subscribing to all events created after a certain time
75
+ # subscription = client.subscribe(filter: Nostr::Filter.new(since: 1230981305))
76
+ #
77
+ # @param subscription_id [String] The subscription id. A random string.
78
+ # @param filter [Filter] A set of attributes that represent the events that the client is interested in.
79
+ #
80
+ # @return [Subscription] The subscription object
81
+ #
82
+ def subscribe(subscription_id: SecureRandom.hex, filter: Filter.new)
83
+ subscriptions[subscription_id] = Subscription.new(id: subscription_id, filter:)
84
+ parent_to_child_channel.push([ClientMessageType::REQ, subscription_id, filter.to_h].to_json)
85
+ subscriptions[subscription_id]
86
+ end
87
+
88
+ # Stops a previous subscription
89
+ #
90
+ # @api public
91
+ #
92
+ # @example Stopping a subscription
93
+ # client.unsubscribe(subscription.id)
94
+ #
95
+ # @example Stopping a subscription
96
+ # client.unsubscribe('my-subscription')
97
+ #
98
+ # @param subscription_id [String] ID of a previously created subscription.
99
+ #
100
+ # @return [void]
101
+ #
102
+ def unsubscribe(subscription_id)
103
+ subscriptions.delete(subscription_id)
104
+ parent_to_child_channel.push([ClientMessageType::CLOSE, subscription_id].to_json)
105
+ end
106
+
107
+ # Sends an event to a Relay
108
+ #
109
+ # @api public
110
+ #
111
+ # @example Sending an event to a relay
112
+ # client.publish(event)
113
+ #
114
+ # @param event [Event] The event to be sent to a Relay
115
+ #
116
+ # @return [void]
117
+ #
118
+ def publish(event)
119
+ parent_to_child_channel.push([ClientMessageType::EVENT, event.to_h].to_json)
120
+ end
121
+
122
+ private
123
+
124
+ # The subscriptions that the client has created
125
+ #
126
+ # @api private
127
+ #
128
+ # @return [Hash{String=>Subscription}>]
129
+ #
130
+ attr_reader :subscriptions
131
+
132
+ # The channel that the parent thread uses to send messages to the child thread
133
+ #
134
+ # @api private
135
+ #
136
+ # @return [EventMachine::Channel]
137
+ #
138
+ attr_reader :parent_to_child_channel
139
+
140
+ # The channel that the child thread uses to send messages to the parent thread
141
+ #
142
+ # @api private
143
+ #
144
+ # @return [EventMachine::Channel]
145
+ #
146
+ attr_reader :child_to_parent_channel
147
+
148
+ # Executes a block of code within the EventMachine thread
149
+ #
150
+ # @api private
151
+ #
152
+ # @return [Thread]
153
+ #
154
+ def execute_within_an_em_thread(&block)
155
+ Thread.new { EventMachine.run(block) }
156
+ end
157
+
158
+ # Creates the communication channels between threads
159
+ #
160
+ # @api private
161
+ #
162
+ # @return [void]
163
+ #
164
+ def initialize_channels
165
+ @parent_to_child_channel = EventMachine::Channel.new
166
+ @child_to_parent_channel = EventMachine::Channel.new
167
+
168
+ child_to_parent_channel.subscribe do |msg|
169
+ emit :connect if msg[:type] == :open
170
+ emit :message, msg[:data] if msg[:type] == :message
171
+ emit :error, msg[:message] if msg[:type] == :error
172
+ emit :close, msg[:code], msg[:reason] if msg[:type] == :close
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nostr
4
+ # Clients can send 3 types of messages, which must be JSON arrays
5
+ module ClientMessageType
6
+ # @return [String] Used to publish events
7
+ EVENT = 'EVENT'
8
+
9
+ # @return [String] Used to request events and subscribe to new updates
10
+ REQ = 'REQ'
11
+
12
+ # @return [String] Used to stop previous subscriptions
13
+ CLOSE = 'CLOSE'
14
+ end
15
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nostr
4
+ # The only object type that exists in Nostr is an event. Events are immutable.
5
+ class Event < EventFragment
6
+ # 32-bytes sha256 of the the serialized event data.
7
+ # To obtain the event.id, we sha256 the serialized event. The serialization is done over the UTF-8 JSON-serialized
8
+ # string (with no white space or line breaks)
9
+ #
10
+ # @api public
11
+ #
12
+ # @example Getting the event id
13
+ # event.id # => 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460'
14
+ #
15
+ # @return [String]
16
+ #
17
+ attr_reader :id
18
+
19
+ # 64-bytes signature of the sha256 hash of the serialized event data, which is
20
+ # the same as the "id" field
21
+ #
22
+ # @api public
23
+ #
24
+ # @example Getting the event signature
25
+ # event.sig # => ''
26
+ #
27
+ # @return [String]
28
+ #
29
+ attr_reader :sig
30
+
31
+ # Instantiates a new Event
32
+ #
33
+ # @api public
34
+ #
35
+ # @example Instantiating a new event
36
+ # Nostr::Event.new(
37
+ # id: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460',
38
+ # pubkey: '48df4af6e240ac5f7c5de89bf5941b249880be0e87d03685b178ccb1a315f52e',
39
+ # created_at: 1230981305,
40
+ # kind: 1,
41
+ # tags: [],
42
+ # content: 'Your feedback is appreciated, now pay $8',
43
+ # sig: '123ac2923b792ce730b3da34f16155470ab13c8f97f9c53eaeb334f1fb3a5dc9a7f643
44
+ # 937c6d6e9855477638f5655c5d89c9aa5501ea9b578a66aced4f1cd7b3'
45
+ # )
46
+ #
47
+ #
48
+ # @param id [String] 32-bytes sha256 of the the serialized event data.
49
+ # @param sig [String] 64-bytes signature of the sha256 hash of the serialized event data, which is
50
+ # the same as the "id" field
51
+ #
52
+ def initialize(id:, sig:, **kwargs)
53
+ super(**kwargs)
54
+
55
+ @id = id
56
+ @sig = sig
57
+ end
58
+
59
+ # Converts the event to a hash
60
+ #
61
+ # @api public
62
+ #
63
+ # @example Converting the event to a hash
64
+ # event.to_h
65
+ #
66
+ # @return [Hash] The event as a hash.
67
+ #
68
+ def to_h
69
+ {
70
+ id:,
71
+ pubkey:,
72
+ created_at:,
73
+ kind:,
74
+ tags:,
75
+ content:,
76
+ sig:
77
+ }
78
+ end
79
+
80
+ # Compares two events. Returns true if all attributes are equal and false otherwise
81
+ #
82
+ # @api public
83
+ #
84
+ # @example
85
+ # event1 == event2 # => true
86
+ #
87
+ # @return [Boolean] True if all attributes are equal and false otherwise
88
+ #
89
+ def ==(other)
90
+ to_h == other.to_h
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nostr
4
+ # Part of an +Event+. A complete +Event+ must have an +id+ and a +sig+.
5
+ class EventFragment
6
+ # 32-bytes hex-encoded public key of the event creator
7
+ #
8
+ # @api public
9
+ #
10
+ # @example
11
+ # event.pubkey # => '48df4af6e240ac5f7c5de89bf5941b249880be0e87d03685b178ccb1a315f52e'
12
+ #
13
+ # @return [String]
14
+ #
15
+ attr_reader :pubkey
16
+
17
+ # Date of the creation of the vent. A UNIX timestamp, in seconds
18
+ #
19
+ # @api public
20
+ #
21
+ # @example
22
+ # event.created_at # => 1230981305
23
+ #
24
+ # @return [Integer]
25
+ #
26
+ attr_reader :created_at
27
+
28
+ # The kind of the event. An integer from 0 to 2
29
+ #
30
+ # @api public
31
+ #
32
+ # @example
33
+ # event.kind # => 1
34
+ #
35
+ # @return [Integer]
36
+ #
37
+ attr_reader :kind
38
+
39
+ # An array of tags. Each tag is an array of strings
40
+ #
41
+ # @api public
42
+ #
43
+ # @example Tags referencing an event
44
+ # event.tags #=> [["e", "event_id", "relay URL"]]
45
+ #
46
+ # @example Tags referencing a key
47
+ # event.tags #=> [["p", "event_id", "relay URL"]]
48
+ #
49
+ # @return [Array<Array>]
50
+ #
51
+ attr_reader :tags
52
+
53
+ # An arbitrary string
54
+ #
55
+ # @api public
56
+ #
57
+ # @example
58
+ # event.content # => 'Your feedback is appreciated, now pay $8'
59
+ #
60
+ # @return [String]
61
+ #
62
+ attr_reader :content
63
+
64
+ # Instantiates a new EventFragment
65
+ #
66
+ # @api public
67
+ #
68
+ # @example
69
+ # Nostr::EventFragment.new(
70
+ # pubkey: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460',
71
+ # created_at: 1230981305,
72
+ # kind: 1,
73
+ # tags: [['e', '189df012cfff8a075785b884bd702025f4a7a37710f581c4ac9d33e24b585408']],
74
+ # content: 'Your feedback is appreciated, now pay $8'
75
+ # )
76
+ #
77
+ # @param pubkey [String] 32-bytes hex-encoded public key of the event creator.
78
+ # @param created_at [Integer] Date of the creation of the vent. A UNIX timestamp, in seconds.
79
+ # @param kind [Integer] The kind of the event. An integer from 0 to 2.
80
+ # @param tags [Array<Array>] An array of tags. Each tag is an array of strings.
81
+ # @param content [String] Arbitrary string.
82
+ #
83
+ def initialize(pubkey:, kind:, content:, created_at: Time.now.to_i, tags: [])
84
+ @pubkey = pubkey
85
+ @created_at = created_at
86
+ @kind = kind
87
+ @tags = tags
88
+ @content = content
89
+ end
90
+
91
+ # Serializes the event fragment, to obtain a SHA256 hash of it
92
+ #
93
+ # @api public
94
+ #
95
+ # @example Converting the event to a hash
96
+ # event_fragment.serialize
97
+ #
98
+ # @return [Array] The event fragment as an array.
99
+ #
100
+ def serialize
101
+ [
102
+ 0,
103
+ pubkey,
104
+ created_at,
105
+ kind,
106
+ tags,
107
+ content
108
+ ]
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nostr
4
+ # Defines the event kinds that can be emitted by clients.
5
+ module EventKind
6
+ # The content is set to a stringified JSON object +{name: <username>, about: <string>,
7
+ # picture: <url, string>}+ describing the user who created the event. A relay may delete past set_metadata
8
+ # events once it gets a new one for the same pubkey.
9
+ #
10
+ # @return [Integer]
11
+ #
12
+ SET_METADATA = 0
13
+
14
+ # The content is set to the text content of a note (anything the user wants to say).
15
+ # Non-plaintext notes should instead use kind 1000-10000 as described in NIP-16.
16
+ #
17
+ # @return [Integer]
18
+ #
19
+ TEXT_NOTE = 1
20
+
21
+ # The content is set to the URL (e.g., wss://somerelay.com) of a relay the event creator wants to
22
+ # recommend to its followers.
23
+ #
24
+ # @return [Integer]
25
+ #
26
+ RECOMMEND_SERVER = 2
27
+ end
28
+ end