nostr 0.4.0 → 0.5.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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +1 -1
  3. data/.rubocop.yml +23 -0
  4. data/.tool-versions +2 -1
  5. data/CHANGELOG.md +36 -1
  6. data/README.md +92 -228
  7. data/docs/.gitignore +4 -0
  8. data/docs/.vitepress/config.mjs +112 -0
  9. data/docs/README.md +44 -0
  10. data/docs/api-examples.md +49 -0
  11. data/docs/bun.lockb +0 -0
  12. data/docs/common-use-cases/bech32-encoding-and-decoding-(NIP-19).md +190 -0
  13. data/docs/core/client.md +108 -0
  14. data/docs/core/keys.md +136 -0
  15. data/docs/core/user.md +43 -0
  16. data/docs/events/contact-list.md +29 -0
  17. data/docs/events/encrypted-direct-message.md +28 -0
  18. data/docs/events/recommend-server.md +32 -0
  19. data/docs/events/set-metadata.md +20 -0
  20. data/docs/events/text-note.md +15 -0
  21. data/docs/events.md +11 -0
  22. data/docs/getting-started/installation.md +21 -0
  23. data/docs/getting-started/overview.md +170 -0
  24. data/docs/implemented-nips.md +9 -0
  25. data/docs/index.md +44 -0
  26. data/docs/markdown-examples.md +85 -0
  27. data/docs/package.json +12 -0
  28. data/docs/relays/connecting-to-a-relay.md +21 -0
  29. data/docs/relays/publishing-events.md +29 -0
  30. data/docs/relays/receiving-events.md +6 -0
  31. data/docs/subscriptions/creating-a-subscription.md +49 -0
  32. data/docs/subscriptions/deleting-a-subscription.md +10 -0
  33. data/docs/subscriptions/filtering-subscription-events.md +115 -0
  34. data/docs/subscriptions/updating-a-subscription.md +4 -0
  35. data/lib/nostr/bech32.rb +203 -0
  36. data/lib/nostr/client.rb +2 -1
  37. data/lib/nostr/crypto.rb +11 -7
  38. data/lib/nostr/errors/error.rb +7 -0
  39. data/lib/nostr/errors/invalid_hrp_error.rb +21 -0
  40. data/lib/nostr/errors/invalid_key_format_error.rb +20 -0
  41. data/lib/nostr/errors/invalid_key_length_error.rb +20 -0
  42. data/lib/nostr/errors/invalid_key_type_error.rb +18 -0
  43. data/lib/nostr/errors/key_validation_error.rb +6 -0
  44. data/lib/nostr/errors.rb +8 -0
  45. data/lib/nostr/event.rb +3 -4
  46. data/lib/nostr/events/encrypted_direct_message.rb +4 -3
  47. data/lib/nostr/filter.rb +4 -4
  48. data/lib/nostr/key.rb +100 -0
  49. data/lib/nostr/key_pair.rb +30 -6
  50. data/lib/nostr/keygen.rb +43 -4
  51. data/lib/nostr/private_key.rb +36 -0
  52. data/lib/nostr/public_key.rb +36 -0
  53. data/lib/nostr/relay_message_type.rb +18 -0
  54. data/lib/nostr/subscription.rb +2 -2
  55. data/lib/nostr/user.rb +17 -8
  56. data/lib/nostr/version.rb +1 -1
  57. data/lib/nostr.rb +6 -0
  58. data/nostr.gemspec +9 -9
  59. data/sig/nostr/bech32.rbs +14 -0
  60. data/sig/nostr/client.rbs +5 -5
  61. data/sig/nostr/crypto.rbs +5 -5
  62. data/sig/nostr/errors/error.rbs +4 -0
  63. data/sig/nostr/errors/invalid_hrb_error.rbs +6 -0
  64. data/sig/nostr/errors/invalid_key_format_error.rbs +5 -0
  65. data/sig/nostr/errors/invalid_key_length_error.rbs +5 -0
  66. data/sig/nostr/errors/invalid_key_type_error.rbs +5 -0
  67. data/sig/nostr/errors/key_validation_error.rbs +4 -0
  68. data/sig/nostr/event.rbs +4 -4
  69. data/sig/nostr/events/encrypted_direct_message.rbs +2 -2
  70. data/sig/nostr/filter.rbs +3 -12
  71. data/sig/nostr/key.rbs +16 -0
  72. data/sig/nostr/key_pair.rbs +7 -3
  73. data/sig/nostr/keygen.rbs +5 -2
  74. data/sig/nostr/private_key.rbs +4 -0
  75. data/sig/nostr/public_key.rbs +4 -0
  76. data/sig/nostr/relay_message_type.rbs +8 -0
  77. data/sig/nostr/user.rbs +4 -8
  78. data/sig/vendor/bech32/nostr/entity.rbs +41 -0
  79. data/sig/vendor/bech32/nostr/nip19.rbs +20 -0
  80. data/sig/vendor/bech32/segwit_addr.rbs +21 -0
  81. data/sig/vendor/bech32.rbs +25 -0
  82. data/sig/vendor/event_emitter.rbs +10 -3
  83. data/sig/vendor/event_machine/channel.rbs +1 -1
  84. data/sig/vendor/faye/websocket/api.rbs +45 -0
  85. data/sig/vendor/faye/websocket/client.rbs +43 -0
  86. data/sig/vendor/faye/websocket.rbs +30 -0
  87. metadata +79 -21
@@ -0,0 +1,21 @@
1
+ # Installation
2
+
3
+ Install the gem and add to the application's Gemfile by executing:
4
+
5
+ ```shell
6
+ bundle add nostr
7
+ ```
8
+
9
+ If bundler is not being used to manage dependencies, install the gem by executing:
10
+
11
+ ```shell
12
+ gem install nostr
13
+ ```
14
+
15
+ ## Requiring the gem
16
+
17
+ All examples in this guide assume that the gem has been required:
18
+
19
+ ```ruby
20
+ require 'nostr'
21
+ ```
@@ -0,0 +1,170 @@
1
+ ---
2
+ editLink: true
3
+ ---
4
+
5
+ # Getting started
6
+
7
+ This gem abstracts the complexity that you would face when trying to connect to relays web sockets, send and receive
8
+ events, handle events callbacks and much more.
9
+
10
+ ## Visual overview
11
+
12
+ Begin your journey with an overview of the essential functions. A visual representation below maps out the key
13
+ components we'll delve into in this section.
14
+
15
+ ```mermaid
16
+ classDiagram
17
+ class Client {
18
+ connect(relay)
19
+ publish(event)
20
+ subscribe(subscription_id, filter)
21
+ unsubscribe(subscription_id)
22
+ }
23
+ class Relay {
24
+ url
25
+ name
26
+ }
27
+ class Event {
28
+ pubkey
29
+ created_at
30
+ kind
31
+ tags
32
+ content
33
+ add_event_reference(event_id)
34
+ add_pubkey_reference(pubkey)
35
+ serialize()
36
+ to_h()
37
+ sign(private_key)
38
+ }
39
+ class Subscription {
40
+ id
41
+ filter
42
+ }
43
+ class Filter {
44
+ ids
45
+ authors
46
+ kinds
47
+ since
48
+ until
49
+ limit
50
+ to_h()
51
+ }
52
+ class EventKind {
53
+ <<Enumeration>>
54
+ SET_METADATA
55
+ TEXT_NOTE
56
+ RECOMMEND_SERVER
57
+ CONTACT_LIST
58
+ ENCRYPTED_DIRECT_MESSAGE
59
+ }
60
+ class KeyPair {
61
+ private_key
62
+ public_key
63
+ }
64
+ class Keygen {
65
+ generate_key_pair()
66
+ generate_private_key()
67
+ extract_public_key(private_key)
68
+ }
69
+ class User {
70
+ keypair
71
+ create_event(event_attributes)
72
+ }
73
+
74
+ Client --> Relay : connects via <br> WebSockets to
75
+ Client --> Event : uses WebSockets to <br> publish and receive
76
+ Client --> Subscription : receives Events via
77
+ Subscription --> Filter : uses
78
+ Event --> EventKind : is of kind
79
+ User --> KeyPair : has
80
+ User --> Event : creates and signs
81
+ User --> Keygen : uses to generate keys
82
+ Keygen --> KeyPair : generates
83
+ ```
84
+
85
+ ## Code overview
86
+
87
+ Explore the provided code snippet to learn about initializing the Nostr [client](../core/client.md), generating
88
+ a [keypair](../core/keys), [publishing](../relays/publishing-events) an event, and
89
+ efficiently [managing event subscriptions](../subscriptions/creating-a-subscription) (including event reception,
90
+ filtering, and WebSocket event handling).
91
+
92
+ ```ruby
93
+ # Require the gem
94
+ require 'nostr'
95
+
96
+ # Instantiate a client
97
+ client = Nostr::Client.new
98
+
99
+ # a) Use an existing keypair
100
+ keypair = Nostr::KeyPair.new(
101
+ private_key: Nostr::PrivateKey.new('your-hex-private-key'),
102
+ public_key: Nostr::PublicKey.new('your-hex-public-key'),
103
+ )
104
+
105
+ # b) Or build a keypair from a private key
106
+ keygen = Nostr::Keygen.new
107
+ keypair = keygen.get_key_pair_from_private_key(
108
+ Nostr::PrivateKey.new('your-hex-private-key')
109
+ )
110
+
111
+ # c) Or create a new keypair
112
+ keygen = Nostr::Keygen.new
113
+ keypair = keygen.generate_keypair
114
+
115
+ # Create a user with the keypair
116
+ user = Nostr::User.new(keypair: keypair)
117
+
118
+ # Create a signed event
119
+ text_note_event = user.create_event(
120
+ kind: Nostr::EventKind::TEXT_NOTE,
121
+ content: 'Your feedback is appreciated, now pay $8'
122
+ )
123
+
124
+ # Connect asynchronously to a relay
125
+ relay = Nostr::Relay.new(url: 'wss://nostr.wine', name: 'Wine')
126
+ client.connect(relay)
127
+
128
+ # Listen asynchronously for the connect event
129
+ client.on :connect do
130
+ # Send the event to the Relay
131
+ client.publish(text_note_event)
132
+
133
+ # Create a filter to receive the first 20 text notes
134
+ # and encrypted direct messages from the relay that
135
+ # were created in the previous hour
136
+ filter = Nostr::Filter.new(
137
+ kinds: [
138
+ Nostr::EventKind::TEXT_NOTE,
139
+ Nostr::EventKind::ENCRYPTED_DIRECT_MESSAGE
140
+ ],
141
+ since: Time.now.to_i - 3600, # 1 hour ago
142
+ until: Time.now.to_i,
143
+ limit: 20,
144
+ )
145
+
146
+ # Subscribe to events matching conditions of a filter
147
+ subscription = client.subscribe(filter: filter)
148
+
149
+ # Unsubscribe from events matching the filter above
150
+ client.unsubscribe(subscription.id)
151
+ end
152
+
153
+ # Listen for incoming messages and print them
154
+ client.on :message do |message|
155
+ puts message
156
+ end
157
+
158
+ # Listen for error messages
159
+ client.on :error do |error_message|
160
+ # Handle the error
161
+ end
162
+
163
+ # Listen for the close event
164
+ client.on :close do |code, reason|
165
+ # You may attempt to reconnect to the relay here
166
+ end
167
+ ```
168
+
169
+ Beyond what's covered here, the Nostr protocol and this gem boast a wealth of additional functionalities. For an
170
+ in-depth exploration of these capabilities, proceed to the next page.
@@ -0,0 +1,9 @@
1
+ # Implemented NIPs
2
+
3
+ NIPs stand for Nostr Implementation Possibilities. They exist to document what may be implemented by Nostr-compatible
4
+ relay and client software.
5
+
6
+ - [NIP-01: Basic protocol flow description](https://github.com/nostr-protocol/nips/blob/master/01.md)
7
+ - [NIP-02: Contact List and Petnames](https://github.com/nostr-protocol/nips/blob/master/02.md)
8
+ - [NIP-04: Encrypted Direct Message](https://github.com/nostr-protocol/nips/blob/master/04.md)
9
+ - [NIP-19: Bech32-encoded entities](https://github.com/nostr-protocol/nips/blob/master/19.md)
data/docs/index.md ADDED
@@ -0,0 +1,44 @@
1
+ ---
2
+ # https://vitepress.dev/reference/default-theme-home-page
3
+ layout: home
4
+
5
+ hero:
6
+ name: "Nostr"
7
+ text: "Ruby"
8
+ tagline: "The Nostr protocol implemented in a Ruby gem."
9
+ actions:
10
+ - theme: brand
11
+ text: Getting Started
12
+ link: /getting-started/overview
13
+ - theme: alt
14
+ text: View on Github
15
+ link: https://github.com/wilsonsilva/nostr
16
+ - theme: alt
17
+ text: View on RubyDoc
18
+ link: https://www.rubydoc.info/gems/nostr
19
+ - theme: alt
20
+ text: View on RubyGems
21
+ link: https://rubygems.org/gems/nostr
22
+
23
+ features:
24
+ - title: Asynchronous
25
+ details: Non-blocking I/O for maximum performance.
26
+ icon: ⚡
27
+ - title: Lightweight
28
+ details: Minimal runtime dependencies.
29
+ icon: 🪶
30
+ - title: Intuitive
31
+ details: The API mirrors the Nostr protocol specification domain language.
32
+ icon: 💡
33
+ - title: Fully documented
34
+ details: All code is documented from both the maintainer's as well as the consumer's perspective.
35
+ icon: 📚
36
+ - title: Fully tested
37
+ details: All code is tested with 100% coverage.
38
+ icon: 🧪
39
+ - title: Fully typed
40
+ details: All code is typed with <a href="https://rubygems.org/gems/rbs" target="_blank">RBS</a> with the help of <a href="https://rubygems.org/gems/typeprof" target="_blank">TypeProf</a>. Type correctness is enforced by <a href="https://rubygems.org/gems/steep" target="_blank">Steep</a>.
41
+ icon: ✅
42
+
43
+ ---
44
+
@@ -0,0 +1,85 @@
1
+ # Markdown Extension Examples
2
+
3
+ This page demonstrates some of the built-in markdown extensions provided by VitePress.
4
+
5
+ ## Syntax Highlighting
6
+
7
+ VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting:
8
+
9
+ **Input**
10
+
11
+ ````
12
+ ```js{4}
13
+ export default {
14
+ data () {
15
+ return {
16
+ msg: 'Highlighted!'
17
+ }
18
+ }
19
+ }
20
+ ```
21
+ ````
22
+
23
+ **Output**
24
+
25
+ ```js{4}
26
+ export default {
27
+ data () {
28
+ return {
29
+ msg: 'Highlighted!'
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ## Custom Containers
36
+
37
+ **Input**
38
+
39
+ ```md
40
+ ::: info
41
+ This is an info box.
42
+ :::
43
+
44
+ ::: tip
45
+ This is a tip.
46
+ :::
47
+
48
+ ::: warning
49
+ This is a warning.
50
+ :::
51
+
52
+ ::: danger
53
+ This is a dangerous warning.
54
+ :::
55
+
56
+ ::: details
57
+ This is a details block.
58
+ :::
59
+ ```
60
+
61
+ **Output**
62
+
63
+ ::: info
64
+ This is an info box.
65
+ :::
66
+
67
+ ::: tip
68
+ This is a tip.
69
+ :::
70
+
71
+ ::: warning
72
+ This is a warning.
73
+ :::
74
+
75
+ ::: danger
76
+ This is a dangerous warning.
77
+ :::
78
+
79
+ ::: details
80
+ This is a details block.
81
+ :::
82
+
83
+ ## More
84
+
85
+ Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown).
data/docs/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "scripts": {
3
+ "docs:dev": "vitepress dev",
4
+ "docs:build": "vitepress build",
5
+ "docs:preview": "vitepress preview"
6
+ },
7
+ "devDependencies": {
8
+ "mermaid": "^10.6.1",
9
+ "vitepress": "^1.0.0-rc.25",
10
+ "vitepress-plugin-mermaid": "^2.0.15"
11
+ }
12
+ }
@@ -0,0 +1,21 @@
1
+ # Connecting to a Relay
2
+
3
+ You must connect your nostr [Client](../core/client) to a relay in order to send and receive [Events](../events).
4
+ Instantiate a [`Nostr::Client`](https://www.rubydoc.info/gems/nostr/Nostr/Client) and a
5
+ [`Nostr::Relay`](https://www.rubydoc.info/gems/nostr/Nostr/Relay) giving it the `url` of your relay. The `name`
6
+ attribute is just descriptive.
7
+ Calling [`Client#connect`](https://www.rubydoc.info/gems/nostr/Nostr/Client#connect-instance_method) attempts to
8
+ establish a WebSocket connection between the Client and the Relay.
9
+
10
+ ```ruby
11
+ client = Nostr::Client.new
12
+ relay = Nostr::Relay.new(url: 'wss://relay.damus.io', name: 'Damus')
13
+
14
+ # Listen for the connect event
15
+ client.on :connect do
16
+ # When this block executes, you're connected to the relay
17
+ end
18
+
19
+ # Connect to a relay asynchronously
20
+ client.connect(relay)
21
+ ```
@@ -0,0 +1,29 @@
1
+ # Publishing events
2
+
3
+ Create a [signed event](../core/keys) and call the method
4
+ [`Nostr::Client#publish`](https://www.rubydoc.info/gems/nostr/Nostr/Client#publish-instance_method) to send the
5
+ event to the relay.
6
+
7
+ ```ruby{4-8,17}
8
+ # Create a user with the keypair
9
+ user = Nostr::User.new(keypair: keypair)
10
+
11
+ # Create a signed event
12
+ text_note_event = user.create_event(
13
+ kind: Nostr::EventKind::TEXT_NOTE,
14
+ content: 'Your feedback is appreciated, now pay $8'
15
+ )
16
+
17
+ # Connect asynchronously to a relay
18
+ relay = Nostr::Relay.new(url: 'wss://nostr.wine', name: 'Wine')
19
+ client.connect(relay)
20
+
21
+ # Listen asynchronously for the connect event
22
+ client.on :connect do
23
+ # Send the event to the relay
24
+ client.publish(text_note_event)
25
+ end
26
+ ```
27
+
28
+ The relay will verify the signature of the event with the public key. If the signature is valid, the relay should
29
+ broadcast the event to all subscribers.
@@ -0,0 +1,6 @@
1
+ # Receiving events
2
+
3
+ To receive events from Relays, you must create a subscription on the relay. A subscription is a filter that defines the
4
+ events you want to receive.
5
+
6
+ For more information, read the [Subscription](../subscriptions/creating-a-subscription.md) section.
@@ -0,0 +1,49 @@
1
+ # Creating a subscription
2
+
3
+ A client can request events and subscribe to new updates __after__ it has established a connection with the Relay.
4
+
5
+ You may use a [`Nostr::Filter`](https://www.rubydoc.info/gems/nostr/Nostr/Filter) instance with as many attributes as
6
+ you wish:
7
+
8
+ ```ruby
9
+ client.on :connect do
10
+ filter = Nostr::Filter.new(
11
+ ids: ['8535d5e2d7b9dc07567f676fbe70428133c9884857e1915f5b1cc6514c2fdff8'],
12
+ authors: ['ae00f88a885ce76afad5cbb2459ef0dcf0df0907adc6e4dac16e1bfbd7074577'],
13
+ kinds: [Nostr::EventKind::TEXT_NOTE],
14
+ e: ["f111593a72cc52a7f0978de5ecf29b4653d0cf539f1fa50d2168fc1dc8280e52"],
15
+ p: ["f1f9b0996d4ff1bf75e79e4cc8577c89eb633e68415c7faf74cf17a07bf80bd8"],
16
+ since: 1230981305,
17
+ until: 1292190341,
18
+ limit: 420,
19
+ )
20
+
21
+ subscription = client.subscribe(subscription_id: 'an-id', filter: filter)
22
+ end
23
+ ```
24
+
25
+ With just a few:
26
+
27
+ ```ruby
28
+ client.on :connect do
29
+ filter = Nostr::Filter.new(kinds: [Nostr::EventKind::TEXT_NOTE])
30
+ subscription = client.subscribe(subscription_id: 'an-id', filter: filter)
31
+ end
32
+ ```
33
+
34
+ Or omit the filter:
35
+
36
+ ```ruby
37
+ client.on :connect do
38
+ subscription = client.subscribe(subscription_id: 'an-id')
39
+ end
40
+ ```
41
+
42
+ Or even omit the subscription id:
43
+
44
+ ```ruby
45
+ client.on :connect do
46
+ subscription = client.subscribe(filter: filter)
47
+ subscription.id # => "13736f08dee8d7b697222ba605c6fab2" (randomly generated)
48
+ end
49
+ ```
@@ -0,0 +1,10 @@
1
+ # Stop previous subscriptions
2
+
3
+ You can stop receiving messages from a subscription by calling
4
+ [`Nostr::Client#unsubscribe`](https://www.rubydoc.info/gems/nostr/Nostr/Client#unsubscribe-instance_method) with the
5
+ ID of the subscription you want to stop receiving messages from:
6
+
7
+ ```ruby
8
+ client.unsubscribe('your-subscription-id')
9
+ client.unsubscribe(subscription.id)
10
+ ```
@@ -0,0 +1,115 @@
1
+ # Filtering events
2
+
3
+ ## Filtering by id
4
+
5
+ You can filter events by their ids:
6
+
7
+ ```ruby
8
+ filter = Nostr::Filter.new(
9
+ ids: [
10
+ # matches events with these exact IDs
11
+ '8535d5e2d7b9dc07567f676fbe70428133c9884857e1915f5b1cc6514c2fdff8',
12
+ '461544014d87c9eaf3e76e021240007dff2c7afb356319f99c741b45749bf82f',
13
+ ]
14
+ )
15
+ subscription = client.subscribe(filter: filter)
16
+ ```
17
+
18
+ ## Filtering by author
19
+
20
+ You can filter events by their author's pubkey:
21
+
22
+ ```ruby
23
+ filter = Nostr::Filter.new(
24
+ authors: [
25
+ # matches events whose (authors) pubkey match these exact IDs
26
+ 'b698043170d580f8ae5bad4ac80b1fdb508e957f0bbffe97f2a8915fa8b34070',
27
+ '51f853ff4894b062950e46ebed8c1c7015160f8173994414a96dd286f65f0f49',
28
+ ]
29
+ )
30
+ subscription = client.subscribe(filter: filter)
31
+ ```
32
+
33
+ ## Filtering by kind
34
+
35
+ You can filter events by their kind:
36
+
37
+ ```ruby
38
+ filter = Nostr::Filter.new(
39
+ kinds: [
40
+ # matches events whose kind is TEXT_NOTE
41
+ Nostr::EventKind::TEXT_NOTE,
42
+ # and matches events whose kind is CONTACT_LIST
43
+ Nostr::EventKind::CONTACT_LIST,
44
+ ]
45
+ )
46
+ subscription = client.subscribe(filter: filter)
47
+ ```
48
+
49
+ ## Filtering by referenced event
50
+
51
+ You can filter events by the events they reference (in their `e` tag):
52
+
53
+ ```ruby
54
+ filter = Nostr::Filter.new(
55
+ e: [
56
+ # matches events that reference other events whose ids match these exact IDs
57
+ 'f111593a72cc52a7f0978de5ecf29b4653d0cf539f1fa50d2168fc1dc8280e52',
58
+ 'f1f9b0996d4ff1bf75e79e4cc8577c89eb633e68415c7faf74cf17a07bf80bd8',
59
+ ]
60
+ )
61
+ subscription = client.subscribe(filter: filter)
62
+ ```
63
+
64
+ ## Filtering by referenced pubkey
65
+
66
+ You can filter events by the pubkeys they reference (in their `p` tag):
67
+
68
+ ```ruby
69
+ filter = Nostr::Filter.new(
70
+ p: [
71
+ # matches events that reference other pubkeys that match these exact IDs
72
+ 'b698043170d580f8ae5bad4ac80b1fdb508e957f0bbffe97f2a8915fa8b34070',
73
+ '51f853ff4894b062950e46ebed8c1c7015160f8173994414a96dd286f65f0f49',
74
+ ]
75
+ )
76
+ subscription = client.subscribe(filter: filter)
77
+ ```
78
+
79
+ ## Filtering by timestamp
80
+
81
+ You can filter events by their timestamp:
82
+
83
+ ```ruby
84
+ filter = Nostr::Filter.new(
85
+ since: 1230981305, # matches events that are newer than this timestamp
86
+ until: 1292190341, # matches events that are older than this timestamp
87
+ )
88
+ subscription = client.subscribe(filter: filter)
89
+ ```
90
+
91
+ ## Limiting the number of events
92
+
93
+ You can limit the number of events received:
94
+
95
+ ```ruby
96
+ filter = Nostr::Filter.new(
97
+ limit: 420, # matches at most 420 events
98
+ )
99
+ subscription = client.subscribe(filter: filter)
100
+ ```
101
+
102
+ ## Combining filters
103
+
104
+ You can combine filters. For example, to match `5` text note events that are newer than `1230981305` from the author
105
+ `ae00f88a885ce76afad5cbb2459ef0dcf0df0907adc6e4dac16e1bfbd7074577`:
106
+
107
+ ```ruby
108
+ filter = Nostr::Filter.new(
109
+ authors: ['ae00f88a885ce76afad5cbb2459ef0dcf0df0907adc6e4dac16e1bfbd7074577'],
110
+ kinds: [Nostr::EventKind::TEXT_NOTE],
111
+ since: 1230981305,
112
+ limit: 5,
113
+ )
114
+ subscription = client.subscribe(filter: filter)
115
+ ```
@@ -0,0 +1,4 @@
1
+ # Updating a subscription
2
+
3
+ Updating a subscription is done by creating a new subscription with the same id as the previous one. See
4
+ [creating a subscription](./creating-a-subscription.md) for more information.