nostr 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.adr-dir +1 -0
  3. data/.editorconfig +1 -1
  4. data/.rubocop.yml +24 -1
  5. data/.tool-versions +2 -1
  6. data/CHANGELOG.md +70 -1
  7. data/README.md +93 -228
  8. data/adr/0001-record-architecture-decisions.md +19 -0
  9. data/adr/0002-introduction-of-signature-class.md +27 -0
  10. data/docs/.gitignore +4 -0
  11. data/docs/.vitepress/config.mjs +114 -0
  12. data/docs/README.md +44 -0
  13. data/docs/api-examples.md +49 -0
  14. data/docs/bun.lockb +0 -0
  15. data/docs/common-use-cases/bech32-encoding-and-decoding-(NIP-19).md +190 -0
  16. data/docs/common-use-cases/signing-and-verifying-events.md +50 -0
  17. data/docs/common-use-cases/signing-and-verifying-messages.md +43 -0
  18. data/docs/core/client.md +108 -0
  19. data/docs/core/keys.md +136 -0
  20. data/docs/core/user.md +43 -0
  21. data/docs/events/contact-list.md +29 -0
  22. data/docs/events/encrypted-direct-message.md +28 -0
  23. data/docs/events/recommend-server.md +32 -0
  24. data/docs/events/set-metadata.md +20 -0
  25. data/docs/events/text-note.md +15 -0
  26. data/docs/events.md +11 -0
  27. data/docs/getting-started/installation.md +21 -0
  28. data/docs/getting-started/overview.md +171 -0
  29. data/docs/implemented-nips.md +9 -0
  30. data/docs/index.md +42 -0
  31. data/docs/markdown-examples.md +85 -0
  32. data/docs/package.json +12 -0
  33. data/docs/relays/connecting-to-a-relay.md +21 -0
  34. data/docs/relays/publishing-events.md +29 -0
  35. data/docs/relays/receiving-events.md +6 -0
  36. data/docs/subscriptions/creating-a-subscription.md +49 -0
  37. data/docs/subscriptions/deleting-a-subscription.md +10 -0
  38. data/docs/subscriptions/filtering-subscription-events.md +115 -0
  39. data/docs/subscriptions/updating-a-subscription.md +4 -0
  40. data/lib/nostr/bech32.rb +203 -0
  41. data/lib/nostr/client.rb +2 -1
  42. data/lib/nostr/crypto.rb +93 -13
  43. data/lib/nostr/errors/error.rb +7 -0
  44. data/lib/nostr/errors/invalid_hrp_error.rb +21 -0
  45. data/lib/nostr/errors/invalid_key_format_error.rb +20 -0
  46. data/lib/nostr/errors/invalid_key_length_error.rb +20 -0
  47. data/lib/nostr/errors/invalid_key_type_error.rb +18 -0
  48. data/lib/nostr/errors/invalid_signature_format_error.rb +18 -0
  49. data/lib/nostr/errors/invalid_signature_length_error.rb +18 -0
  50. data/lib/nostr/errors/invalid_signature_type_error.rb +16 -0
  51. data/lib/nostr/errors/key_validation_error.rb +6 -0
  52. data/lib/nostr/errors/signature_validation_error.rb +6 -0
  53. data/lib/nostr/errors.rb +12 -0
  54. data/lib/nostr/event.rb +40 -13
  55. data/lib/nostr/event_kind.rb +1 -0
  56. data/lib/nostr/events/encrypted_direct_message.rb +8 -7
  57. data/lib/nostr/filter.rb +14 -11
  58. data/lib/nostr/key.rb +100 -0
  59. data/lib/nostr/key_pair.rb +54 -6
  60. data/lib/nostr/keygen.rb +44 -5
  61. data/lib/nostr/private_key.rb +36 -0
  62. data/lib/nostr/public_key.rb +36 -0
  63. data/lib/nostr/relay_message_type.rb +18 -0
  64. data/lib/nostr/signature.rb +67 -0
  65. data/lib/nostr/subscription.rb +2 -2
  66. data/lib/nostr/user.rb +17 -8
  67. data/lib/nostr/version.rb +1 -1
  68. data/lib/nostr.rb +7 -0
  69. data/nostr.gemspec +13 -13
  70. data/sig/nostr/bech32.rbs +14 -0
  71. data/sig/nostr/client.rbs +5 -5
  72. data/sig/nostr/crypto.rbs +8 -5
  73. data/sig/nostr/errors/error.rbs +4 -0
  74. data/sig/nostr/errors/invalid_hrb_error.rbs +6 -0
  75. data/sig/nostr/errors/invalid_key_format_error.rbs +5 -0
  76. data/sig/nostr/errors/invalid_key_length_error.rbs +5 -0
  77. data/sig/nostr/errors/invalid_key_type_error.rbs +5 -0
  78. data/sig/nostr/errors/invalid_signature_format_error.rbs +5 -0
  79. data/sig/nostr/errors/invalid_signature_length_error.rbs +5 -0
  80. data/sig/nostr/errors/invalid_signature_type_error.rbs +5 -0
  81. data/sig/nostr/errors/key_validation_error.rbs +4 -0
  82. data/sig/nostr/errors/signature_validation_error.rbs +4 -0
  83. data/sig/nostr/event.rbs +11 -10
  84. data/sig/nostr/events/encrypted_direct_message.rbs +2 -2
  85. data/sig/nostr/filter.rbs +3 -12
  86. data/sig/nostr/key.rbs +16 -0
  87. data/sig/nostr/key_pair.rbs +8 -3
  88. data/sig/nostr/keygen.rbs +5 -2
  89. data/sig/nostr/private_key.rbs +4 -0
  90. data/sig/nostr/public_key.rbs +4 -0
  91. data/sig/nostr/relay_message_type.rbs +8 -0
  92. data/sig/nostr/signature.rbs +14 -0
  93. data/sig/nostr/user.rbs +4 -8
  94. data/sig/vendor/bech32/nostr/entity.rbs +41 -0
  95. data/sig/vendor/bech32/nostr/nip19.rbs +20 -0
  96. data/sig/vendor/bech32/segwit_addr.rbs +21 -0
  97. data/sig/vendor/bech32.rbs +25 -0
  98. data/sig/vendor/event_emitter.rbs +10 -3
  99. data/sig/vendor/event_machine/channel.rbs +1 -1
  100. data/sig/vendor/faye/websocket/api.rbs +45 -0
  101. data/sig/vendor/faye/websocket/client.rbs +43 -0
  102. data/sig/vendor/faye/websocket.rbs +30 -0
  103. data/sig/vendor/schnorr/signature.rbs +16 -0
  104. data/sig/vendor/schnorr.rbs +3 -1
  105. metadata +102 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd56d59d68235fe8fa4bceb67df4a7e83d4b6a8295ed31cc0152002800ad7d23
4
- data.tar.gz: 56b70ada7f9fd6cd29be1c7bc23b7a23adc49bfe2f8d8de48300f70709626a3e
3
+ metadata.gz: dcfeb88b78cad383e565b6e908be58f58e6f1002083044957ee0f09cf63228f4
4
+ data.tar.gz: ca2229181586e8bf5f329e4d5ac60ad346076fc8e39281dd43690d57ac98fcb7
5
5
  SHA512:
6
- metadata.gz: be113a67cd651739cc3bac1deb4bba30e3598cfd787e8971a764e82d58c03ad1c918d3edd14630bbb1a5e2c6504c3c344859f0449a1774df7bb7bf0d3c38f537
7
- data.tar.gz: c80bbf1f4d3caa25f48e1630b650587a2319197d7edba33e824b17e93f6355d8593c41364ad1fdb86a834d8cae63ad9f15e8feacae953b0ff8d7294f09c2c765
6
+ metadata.gz: 4511decc634548b75429db9166433060795295aea61cda64cd7b2b2647415db3808f4def27f8a4806d77b6fbaef88b11d08788254febd66bcfb7be318059cced
7
+ data.tar.gz: aef568e9fad07e0b387e3ae1437a50d08c81b59485c8aadd42cd9d7a035319b3da2bd5247e7d58fc890961eec5e0284d9299e6631c674cce3df3a5c210202bf2
data/.adr-dir ADDED
@@ -0,0 +1 @@
1
+ adr
data/.editorconfig CHANGED
@@ -6,6 +6,6 @@ max_line_length = 120
6
6
  trim_trailing_whitespace = true
7
7
 
8
8
  # 2 space indentation
9
- [*.rb]
9
+ [{*.rb, *.mjs}]
10
10
  indent_style = space
11
11
  indent_size = 2
data/.rubocop.yml CHANGED
@@ -5,12 +5,21 @@ require:
5
5
  - rubocop-rspec
6
6
 
7
7
  AllCops:
8
- TargetRubyVersion: 3.2
8
+ TargetRubyVersion: 3.3
9
9
  DisplayCopNames: true
10
10
  NewCops: enable
11
11
 
12
+ # ----------------------- Gemspec -----------------------
13
+
14
+ Gemspec/DevelopmentDependencies:
15
+ Enabled: false
16
+
12
17
  # ----------------------- Style -----------------------
13
18
 
19
+ Style/RaiseArgs:
20
+ Exclude:
21
+ - 'lib/nostr/key.rb'
22
+
14
23
  Style/StringLiterals:
15
24
  Enabled: true
16
25
  EnforcedStyle: single_quotes
@@ -38,3 +47,17 @@ Metrics/ParameterLists:
38
47
 
39
48
  RSpec/ExampleLength:
40
49
  Enabled: false
50
+
51
+ RSpec/FilePath:
52
+ Exclude:
53
+ - spec/nostr/errors/invalid_*
54
+
55
+ RSpec/SpecFilePathFormat:
56
+ Exclude:
57
+ - spec/nostr/errors/invalid_*
58
+
59
+ # ----------------------- Naming -----------------------
60
+
61
+ Naming/MemoizedInstanceVariableName:
62
+ Exclude:
63
+ - 'spec/nostr/key.rb'
data/.tool-versions CHANGED
@@ -1 +1,2 @@
1
- ruby 3.2.0
1
+ ruby 3.3.0
2
+ bun 1.0.30
data/CHANGELOG.md CHANGED
@@ -1,9 +1,75 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.1/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.0] 2024-03-15
8
+
9
+ ### Added
10
+
11
+ - Added Architecture Decision Records (ADRs) to document architectural decisions
12
+ - Added the `Signature` class to fix the primitive obsession with signatures and to make it easier to work with them
13
+ - Added `valid_sig?` and `check_sig!` to the `Crypto` class to verify whether an event's signature is valid
14
+ - Added `sign_message` to the `Crypto` class to sign a message
15
+ - Added `verify_signature?` to the `Event` class to verify whether an event's signature is valid
16
+ - Added `#to_ary` to the `KeyPair` class to enable keypair destructuring
17
+ - Added RBS types for `schnorr`
18
+
19
+ ### Changed
20
+
21
+ - Updated the required Ruby version to `3.3.0` (was `3.2.0`)
22
+ - Updated the gem `dotenv` to version `3.1` (was `2.8`)
23
+ - Updated the gem `bip-schnorr` to version `0.7` (was `0.6`)
24
+ - Updated the gem `overcommit` to version `0.63` (was `0.59`)
25
+ - Updated the gem `rbs` to version `3.4` (was `3.3`)
26
+ - Updated the gem `rspec` to version `3.13` (was `3.12`)
27
+ - Updated the gem `rspec-rubocop` to version `2.27` (was `2.25`)
28
+ - Updated the gem `rubocop` to version `1.62` (was `1.57`)
29
+
30
+ ## Fixed
31
+
32
+ - Fixed a typo in the README (`generate_keypair` -> `generate_key_pair`)
33
+ - Fixed a typo in the YARD documentation of `Nostr::Key#initialize` (`ValidationError` -> `KeyValidationError`)
34
+
35
+ ### Fixed
36
+
37
+ - Fixed YARD example rendering issues in `InvalidKeyFormatError#initialize`, `InvalidKeyLengthError#initialize`,
38
+ `InvalidKeyTypeError#initialize`, `Event#initialize`, `EncryptedDirectMessage#initialize` and `Filter#to_h`
39
+
40
+ ## [0.5.0] 2023-11-20
41
+
42
+ ### Added
43
+
44
+ - Added relay message type enums `Nostr::RelayMessageType`
45
+ - Compliance with [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) - bech32-formatted strings
46
+ - Added `Nostr::PrivateKey` and `Nostr::PublicKey` to represent private and public keys, respectively
47
+ - Added a validation of private and public keys
48
+ - Added an ability to convert keys to and from Bech32 format
49
+ - Added RBS types for `faye-websocket` and `bech32`
50
+
51
+ ### Changed
52
+
53
+ - Set the gem's homepage to [`https://nostr-ruby.com/`](https://nostr-ruby.com/)
54
+ - Updated the filter's documentation to reflect the removal of prefix matching
55
+ - Updated the subscription's id documentation to reflect the changes in the protocol definition
56
+ - Updated `Nostr::PrivateKey` and `Nostr::PublicKey` internally, instead of Strings
57
+ - Updated the gem `bip-schnorr` to version `0.6` (was `0.4`)
58
+ - Updated the gem `puma` to version `6.4` (was `6.3`)
59
+ - Updated the gem `rake` to version `13.1` (was `13.0`)
60
+ - Updated the gem `rbs` to version `3.3` (was `2.8`)
61
+ - Updated the gem `rubocop` to version `1.57` (was `1.42`)
62
+ - Updated the gem `rubocop-rspec` to version `2.25` (was `2.16`)
63
+ - Updated the gem `steep` to version `1.6` (was `1.4`)
64
+
65
+ ## Fixed
66
+
67
+ - Fixed the RBS type of the constant `Nostr::Crypto::BN_BASE`
68
+ - Fixed the return type of `Nostr::Crypto#decrypt_text` when given an invalid ciphertext
69
+ - Fixed the RBS type of `Nostr::Filter#to_h`, `Nostr::Filter#e` and `Nostr::Filter#p`
70
+ - Fixed the RBS types of `EventEmitter` and `EventMachine::Channel`
71
+ - Fixed the generation of private keys
72
+
7
73
  ## [0.4.0] - 2023-02-25
8
74
 
9
75
  ### Removed
@@ -49,6 +115,9 @@ principles of immutability and was a major source of internal complexity as I ne
49
115
 
50
116
  - Initial release
51
117
 
118
+ [0.6.0]: https://github.com/wilsonsilva/nostr/compare/v0.5.0...v0.6.0
119
+ [0.5.0]: https://github.com/wilsonsilva/nostr/compare/v0.4.0...v0.5.0
120
+ [0.4.0]: https://github.com/wilsonsilva/nostr/compare/v0.3.0...v0.4.0
52
121
  [0.3.0]: https://github.com/wilsonsilva/nostr/compare/v0.2.0...v0.3.0
53
122
  [0.2.0]: https://github.com/wilsonsilva/nostr/compare/v0.1.0...v0.2.0
54
123
  [0.1.0]: https://github.com/wilsonsilva/nostr/compare/7fded5...v0.1.0
data/README.md CHANGED
@@ -1,34 +1,34 @@
1
1
  # Nostr
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/nostr.svg)](https://badge.fury.io/rb/nostr)
4
+ ![Build](https://github.com/wilsonsilva/nostr/actions/workflows/main.yml/badge.svg)
4
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/c7633eb2c89eb95ee7f2/maintainability)](https://codeclimate.com/github/wilsonsilva/nostr/maintainability)
5
6
  [![Test Coverage](https://api.codeclimate.com/v1/badges/c7633eb2c89eb95ee7f2/test_coverage)](https://codeclimate.com/github/wilsonsilva/nostr/test_coverage)
6
7
 
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.
8
+ Asynchronous Nostr client for Rubyists.
9
9
 
10
10
  ## Table of contents
11
11
 
12
- - [Installation](#installation)
13
- - [Usage](#usage)
14
- * [Requiring the gem](#requiring-the-gem)
15
- * [Generating a keypair](#generating-a-keypair)
16
- * [Generating a private key and a public key](#generating-a-private-key-and-a-public-key)
17
- * [Connecting to a Relay](#connecting-to-a-relay)
18
- * [WebSocket events](#websocket-events)
19
- * [Requesting for events / creating a subscription](#requesting-for-events--creating-a-subscription)
20
- * [Stop previous subscriptions](#stop-previous-subscriptions)
21
- * [Publishing an event](#publishing-an-event)
22
- * [Creating/updating your contact list](#creatingupdating-your-contact-list)
23
- * [Sending an encrypted direct message](#sending-an-encrypted-direct-message)
24
- - [Implemented NIPs](#implemented-nips)
25
- - [Development](#development)
12
+ - [Key features](#-key-features)
13
+ - [Installation](#-installation)
14
+ - [Quickstart](#-quickstart)
15
+ - [Documentation](#-documentation)
16
+ - [Implemented NIPs](#-implemented-nips)
17
+ - [Development](#-development)
26
18
  * [Type checking](#type-checking)
27
- - [Contributing](#contributing)
28
- - [License](#license)
29
- - [Code of Conduct](#code-of-conduct)
19
+ - [Contributing](#-contributing)
20
+ - [License](#-license)
21
+ - [Code of Conduct](#-code-of-conduct)
30
22
 
31
- ## Installation
23
+ ## 🔑 Key features
24
+
25
+ - Asynchronous
26
+ - Easy to use
27
+ - Fully documented
28
+ - Fully tested
29
+ - Fully typed
30
+
31
+ ## 📦 Installation
32
32
 
33
33
  Install the gem and add to the application's Gemfile by executing:
34
34
 
@@ -38,243 +38,103 @@ If bundler is not being used to manage dependencies, install the gem by executin
38
38
 
39
39
  $ gem install nostr
40
40
 
41
- ## Usage
41
+ ## ⚡️ Quickstart
42
42
 
43
- ### Requiring the gem
44
-
45
- All examples below assume that the gem has been required.
43
+ Here is a quick example of how to use the gem. For more detailed documentation, please check the
44
+ [documentation website](https://nostr-ruby.com).
46
45
 
47
46
  ```ruby
47
+ # Require the gem
48
48
  require 'nostr'
49
- ```
50
-
51
- ### Generating a keypair
52
-
53
- ```ruby
54
- keygen = Nostr::Keygen.new
55
- keypair = keygen.generate_key_pair
56
-
57
- keypair.private_key
58
- keypair.public_key
59
- ```
60
-
61
- ### Generating a private key and a public key
62
49
 
63
- ```ruby
64
- keygen = Nostr::Keygen.new
50
+ # Instantiate a client
51
+ client = Nostr::Client.new
65
52
 
66
- private_key = keygen.generate_private_key
67
- public_key = keygen.extract_public_key(private_key)
68
- ```
53
+ # a) Use an existing keypair
54
+ keypair = Nostr::KeyPair.new(
55
+ private_key: Nostr::PrivateKey.new('add-your-hex-private-key-here'),
56
+ public_key: Nostr::PublicKey.new('add-your-hex-public-key-here'),
57
+ )
69
58
 
70
- ### Connecting to a Relay
59
+ # b) Or build a keypair from a private key
60
+ keygen = Nostr::Keygen.new
61
+ keypair = keygen.get_key_pair_from_private_key(
62
+ Nostr::PrivateKey.new('add-your-hex-private-key-here')
63
+ )
71
64
 
72
- Clients can connect to multiple Relays. In this version, a Client can only connect to a single Relay at a time.
65
+ # c) Or create a new keypair
66
+ keygen = Nostr::Keygen.new
67
+ keypair = keygen.generate_key_pair
73
68
 
74
- You may instantiate multiple Clients and multiple Relays.
69
+ # Create a user with the keypair
70
+ user = Nostr::User.new(keypair: keypair)
75
71
 
76
- ```ruby
77
- client = Nostr::Client.new
78
- relay = Nostr::Relay.new(url: 'wss://relay.damus.io', name: 'Damus')
72
+ # Create a signed event
73
+ text_note_event = user.create_event(
74
+ kind: Nostr::EventKind::TEXT_NOTE,
75
+ content: 'Your feedback is appreciated, now pay $8'
76
+ )
79
77
 
78
+ # Connect asynchronously to a relay
79
+ relay = Nostr::Relay.new(url: 'wss://nostr.wine', name: 'Wine')
80
80
  client.connect(relay)
81
- ```
82
-
83
- ### WebSocket events
84
-
85
- All communication between clients and relays happen in WebSockets.
86
-
87
- The `:connect` event is fired when a connection with a WebSocket is opened. You must call `Nostr::Client#connect` first.
88
81
 
89
- ```ruby
82
+ # Listen asynchronously for the connect event
90
83
  client.on :connect do
91
- # all the code goes here
92
- end
93
- ```
94
-
95
- The `:close` event is fired when a connection with a WebSocket has been closed because of an error.
96
-
97
- ```ruby
98
- client.on :error do |error_message|
99
- puts error_message
100
- end
101
-
102
- # > Network error: wss://rsslay.fiatjaf.com: Unable to verify the server certificate for 'rsslay.fiatjaf.com'
103
- ```
104
-
105
- The `:message` event is fired when data is received through a WebSocket.
106
-
107
- ```ruby
108
- client.on :message do |message|
109
- puts message
110
- end
111
-
112
- # [
113
- # "EVENT",
114
- # "d34107357089bfc9882146d3bfab0386",
115
- # {
116
- # "content":"",
117
- # "created_at":1676456512,
118
- # "id":"18f63550da74454c5df7caa2a349edc5b2a6175ea4c5367fa4b4212781e5b310",
119
- # "kind":3,
120
- # "pubkey":"117a121fa41dc2caa0b3d6c5b9f42f90d114f1301d39f9ee96b646ebfee75e36",
121
- # "sig":"d171420bd62cf981e8f86f2dd8f8f86737ea2bbe2d98da88db092991d125535860d982139a3c4be39886188613a9912ef380be017686a0a8b74231dc6e0b03cb",
122
- # "tags":[
123
- # ["p","1cc821cc2d47191b15fcfc0f73afed39a86ac6fb34fbfa7993ee3e0f0186ef7c"]
124
- # ]
125
- # }
126
- # ]
127
- ```
128
-
129
- The `:close` event is fired when a connection with a WebSocket is closed.
130
-
131
- ```ruby
132
- client.on :close do |code, reason|
133
- # you may attempt to reconnect
134
-
135
- client.connect(relay)
136
- end
137
- ```
138
-
139
- ### Requesting for events / creating a subscription
140
-
141
- A client can request events and subscribe to new updates after it has established a connection with the Relay.
84
+ # Send the event to the Relay
85
+ client.publish(text_note_event)
142
86
 
143
- You may use a `Nostr::Filter` instance with as many attributes as you wish:
144
-
145
- ```ruby
146
- client.on :connect do
87
+ # Create a filter to receive the first 20 text notes
88
+ # and encrypted direct messages from the relay that
89
+ # were created in the previous hour
147
90
  filter = Nostr::Filter.new(
148
- ids: ['8535d5e2d7b9dc07567f676fbe70428133c9884857e1915f5b1cc6514c2fdff8'],
149
- authors: ['ae00f88a885ce76afad5cbb2459ef0dcf0df0907adc6e4dac16e1bfbd7074577'],
150
- kinds: [Nostr::EventKind::TEXT_NOTE],
151
- e: ["f111593a72cc52a7f0978de5ecf29b4653d0cf539f1fa50d2168fc1dc8280e52"],
152
- p: ["f1f9b0996d4ff1bf75e79e4cc8577c89eb633e68415c7faf74cf17a07bf80bd8"],
153
- since: 1230981305,
154
- until: 1292190341,
155
- limit: 420,
91
+ kinds: [
92
+ Nostr::EventKind::TEXT_NOTE,
93
+ Nostr::EventKind::ENCRYPTED_DIRECT_MESSAGE
94
+ ],
95
+ since: Time.now.to_i - 3600, # 1 hour ago
96
+ until: Time.now.to_i,
97
+ limit: 20,
156
98
  )
157
99
 
158
- subscription = client.subscribe('a_random_subscription_id', filter)
159
- end
160
- ```
161
-
162
- With just a few:
100
+ # Subscribe to events matching conditions of a filter
101
+ subscription = client.subscribe(filter: filter)
163
102
 
164
- ```ruby
165
- client.on :connect do
166
- filter = Nostr::Filter.new(kinds: [Nostr::EventKind::TEXT_NOTE])
167
- subscription = client.subscribe('a_random_subscription_id', filter)
103
+ # Unsubscribe from events matching the filter above
104
+ client.unsubscribe(subscription.id)
168
105
  end
169
- ```
170
-
171
- Or omit the filter:
172
106
 
173
- ```ruby
174
- client.on :connect do
175
- subscription = client.subscribe('a_random_subscription_id')
107
+ # Listen for incoming messages and print them
108
+ client.on :message do |message|
109
+ puts message
176
110
  end
177
- ```
178
111
 
179
- Or even omit the subscription id:
180
-
181
- ```ruby
182
- client.on :connect do
183
- subscription = client.subscribe('a_random_subscription_id')
112
+ # Listen for error messages
113
+ client.on :error do |error_message|
114
+ # Handle the error
184
115
  end
185
- ```
186
-
187
- ### Stop previous subscriptions
188
-
189
- You can stop receiving messages from a subscription by calling `#unsubscribe`:
190
-
191
- ```ruby
192
- client.unsubscribe('your_subscription_id')
193
- ```
194
-
195
- ### Publishing an event
196
116
 
197
- To publish an event you need a keypair.
198
-
199
- ```ruby
200
- # Set up the private key
201
- private_key = 'a630b06e2f883378d0aa335b9adaf7734603e00433350b684fe53e184f08c58f'
202
- user = Nostr::User.new(private_key)
203
-
204
- # Create a signed event
205
- event = user.create_event(
206
- created_at: 1667422587, # optional, defaults to the current time
207
- kind: Nostr::EventKind::TEXT_NOTE,
208
- tags: [], # optional, defaults to []
209
- content: 'Your feedback is appreciated, now pay $8'
210
- )
211
-
212
- # Send it to the Relay
213
- client.publish(event)
214
- ```
215
-
216
- ### Creating/updating your contact list
217
-
218
- Every new contact list that gets published overwrites the past ones, so it should contain all entries.
219
-
220
- ```ruby
221
- # Creating a contact list event with 2 contacts
222
- update_contacts_event = user.create_event(
223
- kind: Nostr::EventKind::CONTACT_LIST,
224
- tags: [
225
- [
226
- "p", # mandatory
227
- "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245", # public key of the user to add to the contacts
228
- "wss://alicerelay.com/", # can be an empty string or can be omitted
229
- "alice" # can be an empty string or can be omitted
230
- ],
231
- [
232
- "p", # mandatory
233
- "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681", # public key of the user to add to the contacts
234
- "wss://bobrelay.com/nostr", # can be an empty string or can be omitted
235
- "bob" # can be an empty string or can be omitted
236
- ],
237
- ],
238
- )
239
-
240
- # Send it to the Relay
241
- client.publish(update_contacts_event)
117
+ # Listen for the close event
118
+ client.on :close do |code, reason|
119
+ # You may attempt to reconnect to the relay here
120
+ end
242
121
  ```
243
122
 
244
- ### Sending an encrypted direct message
123
+ ## 📚 Documentation
245
124
 
246
- ```ruby
247
- sender_private_key = '3185a47e3802f956ca5a2b4ea606c1d51c7610f239617e8f0f218d55bdf2b757'
125
+ I made a detailed documentation for this gem and it's usage. The code is also fully documented using YARD.
248
126
 
249
- encrypted_direct_message = Nostr::Events::EncryptedDirectMessage.new(
250
- sender_private_key: sender_private_key,
251
- recipient_public_key: '6c31422248998e300a1a457167565da7d15d0da96651296ee2791c29c11b6aa0',
252
- plain_text: 'Your feedback is appreciated, now pay $8',
253
- previous_direct_message: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460' # optional
254
- )
255
-
256
- encrypted_direct_message.sign(sender_private_key)
257
-
258
- # #<Nostr::Events::EncryptedDirectMessage:0x0000000104c9fa68
259
- # @content="mjIFNo1sSP3KROE6QqhWnPSGAZRCuK7Np9X+88HSVSwwtFyiZ35msmEVoFgRpKx4?iv=YckChfS2oWCGpMt1uQ4GbQ==",
260
- # @created_at=1676456512,
261
- # @id="daac98826d5eb29f7c013b6160986c4baf4fe6d4b995df67c1b480fab1839a9b",
262
- # @kind=4,
263
- # @pubkey="8a9d69c56e3c691bec8f9565e4dcbe38ae1d88fffeec3ce66b9f47558a3aa8ca",
264
- # @sig="028bb5f5bab0396e2065000c84a4bcce99e68b1a79bb1b91a84311546f49c5b67570b48d4a328a1827e7a8419d74451347d4f55011a196e71edab31aa3d6bdac",
265
- # @tags=[["p", "6c31422248998e300a1a457167565da7d15d0da96651296ee2791c29c11b6aa0"], ["e", "ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460"]]>
127
+ - [Guide documentation](https://nostr-ruby.com)
128
+ - [YARD documentation](https://rubydoc.info/gems/nostr)
266
129
 
267
- # Send it to the Relay
268
- client.publish(encrypted_direct_message)
269
- ````
130
+ ## Implemented NIPs
270
131
 
271
- ## Implemented NIPs
132
+ - [x] [NIP-01 - Basic protocol flow description](https://github.com/nostr-protocol/nips/blob/master/01.md)
133
+ - [x] [NIP-02 - Contact List and Petnames](https://github.com/nostr-protocol/nips/blob/master/02.md)
134
+ - [x] [NIP-04 - Encrypted Direct Message](https://github.com/nostr-protocol/nips/blob/master/04.md)
135
+ - [x] [NIP-19 - Bech32-encoded entities](https://github.com/nostr-protocol/nips/blob/master/19.md)
272
136
 
273
- - [x] [NIP-01 - Client](https://github.com/nostr-protocol/nips/blob/master/01.md)
274
- - [x] [NIP-02 - Client](https://github.com/nostr-protocol/nips/blob/master/02.md)
275
- - [x] [NIP-04 - Client](https://github.com/nostr-protocol/nips/blob/master/04.md)
276
-
277
- ## Development
137
+ ## 🔨 Development
278
138
 
279
139
  After checking out the repo, run `bin/setup` to install dependencies.
280
140
 
@@ -318,17 +178,22 @@ used to provide type checking and autocompletion in your editor. Run `bundle exe
318
178
  an RBS definition for the given Ruby file. And validate all definitions using [Steep](https://github.com/soutaro/steep)
319
179
  with the command `bundle exec steep check`.
320
180
 
321
- ## Contributing
181
+ ## 🐞 Issues & Bugs
182
+
183
+ If you find any issues or bugs, please report them [here](https://github.com/wilsonsilva/nostr/issues), I will be happy
184
+ to have a look at them and fix them as soon as possible.
185
+
186
+ ## 🤝 Contributing
322
187
 
323
188
  Bug reports and pull requests are welcome on GitHub at https://github.com/wilsonsilva/nostr.
324
189
  This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere
325
190
  to the [code of conduct](https://github.com/wilsonsilva/nostr/blob/main/CODE_OF_CONDUCT.md).
326
191
 
327
- ## License
192
+ ## 📜 License
328
193
 
329
194
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
330
195
 
331
- ## Code of Conduct
196
+ ## 👔 Code of Conduct
332
197
 
333
198
  Everyone interacting in the Nostr project's codebases, issue trackers, chat rooms and mailing lists is expected
334
199
  to follow the [code of conduct](https://github.com/wilsonsilva/nostr/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,19 @@
1
+ # 1. Record architecture decisions
2
+
3
+ Date: 2024-03-13
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ We need to record the architectural decisions made on this project.
12
+
13
+ ## Decision
14
+
15
+ We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
16
+
17
+ ## Consequences
18
+
19
+ See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
@@ -0,0 +1,27 @@
1
+ # 2. introduction-of-signature-class
2
+
3
+ Date: 2024-03-14
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ I noticed significant overuse of primitive strings for signatures, which led to widespread and repetitive validation logic, increasing the potential for errors and making the system harder to manage and maintain.
12
+
13
+ ## Decision
14
+
15
+ I introduced the Nostr::Signature class, choosing to subclass String to leverage string-like behavior while embedding specific validation rules for signatures. This move was aimed at streamlining validation, ensuring consistency, and maintaining the usability of strings.
16
+
17
+ ## Consequences
18
+
19
+ ### Positive
20
+
21
+ - This design choice has made the codebase cleaner and more robust, reducing the chances of errors related to signature handling. It ensures that all signature instances are valid at creation, leveraging the familiarity and flexibility of string operations without sacrificing the integrity of the data. Moreover, it sets a strong foundation for extending signature-related functionality in the future.
22
+
23
+ ### Negative
24
+
25
+ - __Performance Concerns:__ Subclassing String might introduce slight performance overheads due to the additional validation logic executed upon instantiation of a Signature object.
26
+ - __Integration Challenges:__ Integrating this class into existing systems where strings were used indiscriminately for signatures requires careful refactoring to ensure compatibility. There's also the potential for issues when passing Nostr::Signature objects to libraries or APIs expecting plain strings without the additional constraints.
27
+ - __Learning Curve:__ For new team members or contributors, understanding the necessity and functionality of the Nostr::Signature class adds to the learning curve, potentially slowing down initial development efforts as they familiarize themselves with the custom implementation.
data/docs/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /node_modules/
2
+ .vitepress/dist
3
+ .vitepress/temp
4
+ .vitepress/cache