nostr 0.6.0 → 0.7.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: dcfeb88b78cad383e565b6e908be58f58e6f1002083044957ee0f09cf63228f4
4
- data.tar.gz: ca2229181586e8bf5f329e4d5ac60ad346076fc8e39281dd43690d57ac98fcb7
3
+ metadata.gz: 707a302ca79f44bd669a84dc9086c5934bc6f753a78d90989f4d3619db5ed997
4
+ data.tar.gz: 81a83ff089c864ac5c77bc7d4b26a23755ff2573ee013f68137a79ed9351f140
5
5
  SHA512:
6
- metadata.gz: 4511decc634548b75429db9166433060795295aea61cda64cd7b2b2647415db3808f4def27f8a4806d77b6fbaef88b11d08788254febd66bcfb7be318059cced
7
- data.tar.gz: aef568e9fad07e0b387e3ae1437a50d08c81b59485c8aadd42cd9d7a035319b3da2bd5247e7d58fc890961eec5e0284d9299e6631c674cce3df3a5c210202bf2
6
+ metadata.gz: 69bf9a378add76760a6955967787602e1bf6e913f22c1194fa9b8d3dc661270b038f26010bb8181378b7d21586b6d5195fe12754007551baa78531b3e4663e16
7
+ data.tar.gz: a49b8eb1acc9978a1f95be74783253d44807078fc97af2255239067c77125a7a31661514e7196f27d18c76d57805c293c54d5cc0c9427cd792c1f0e65349a796
data/.rubocop.yml CHANGED
@@ -8,6 +8,8 @@ AllCops:
8
8
  TargetRubyVersion: 3.3
9
9
  DisplayCopNames: true
10
10
  NewCops: enable
11
+ Exclude:
12
+ - docs/**/*
11
13
 
12
14
  # ----------------------- Gemspec -----------------------
13
15
 
data/.rubocop_todo.yml CHANGED
@@ -9,7 +9,7 @@
9
9
  # Offense count: 2
10
10
  # Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
11
11
  Metrics/AbcSize:
12
- Max: 23
12
+ Max: 24
13
13
 
14
14
  # Offense count: 2
15
15
  # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
data/.tool-versions CHANGED
@@ -1,2 +1,2 @@
1
1
  ruby 3.3.0
2
- bun 1.0.30
2
+ bun 1.1.3
data/CHANGELOG.md CHANGED
@@ -4,6 +4,36 @@ 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.1.1/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.7.0] 2024-04-13
8
+
9
+ ### Added
10
+
11
+ - Added the `Nostr::Client::Logger` class to log connection events, messages sent and received, errors, and connection
12
+ closures.
13
+ - Added the `Nostr::Client::ColorLogger` class to log events in color using ANSI escape codes.
14
+ - Added the `Nostr::Client::PlainLogger` class to log events without color coding.
15
+ - Added Architecture Decision Records (ADRs) to document the decision process for logging functionality.
16
+ - Added a new common use case document for logging and debugging.
17
+
18
+ ### Changed
19
+
20
+ - Updated the `Nostr::Client` class to use the `ColorLogger` by default for logging client interactions with relays.
21
+ - Updated the `Nostr::Client#connect` method to emit a `:send` event when sending a message.
22
+ - Updated the `Nostr::Client#on` method to pass the `relay` parameter to the `:connect` event handler.
23
+ - Updated bun to version `1.1.3` (was `1.0.30`).
24
+ - Updated the gem `json` to version `2.7` (was `2.6`).
25
+ - Updated the gem `mermaid` to version `10.9` (was `10.6`).
26
+ - Updated the gem `rubocop-rspec` to version `2.29` (was `2.27`).
27
+ - Updated the gem `steep` to version `1.7.dev3` (was `1.6`).
28
+ - Updated the gem `vitepress` to version `1.1` (was `1.0.0-rc.25`).
29
+ - Updated the gem `vitepress-plugin-mermaid` to version `2.0.16` (was `2.0.15`).
30
+ - Updated the error message in `Nostr::InvalidSignatureTypeError` to provide more detail.
31
+
32
+ ### Fixed
33
+
34
+ Fixed a type-checking issue in `Nostr::Event#verify_signature` by removing a workaround after the steep gem author,
35
+ [@soutaro](https://github.com/soutaro) resolved [the problem I reported](https://github.com/soutaro/steep/issues/1079).
36
+
7
37
  ## [0.6.0] 2024-03-15
8
38
 
9
39
  ### Added
@@ -27,13 +57,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
27
57
  - Updated the gem `rspec-rubocop` to version `2.27` (was `2.25`)
28
58
  - Updated the gem `rubocop` to version `1.62` (was `1.57`)
29
59
 
30
- ## Fixed
60
+ ### Fixed
31
61
 
32
62
  - Fixed a typo in the README (`generate_keypair` -> `generate_key_pair`)
33
63
  - Fixed a typo in the YARD documentation of `Nostr::Key#initialize` (`ValidationError` -> `KeyValidationError`)
34
-
35
- ### Fixed
36
-
37
64
  - Fixed YARD example rendering issues in `InvalidKeyFormatError#initialize`, `InvalidKeyLengthError#initialize`,
38
65
  `InvalidKeyTypeError#initialize`, `Event#initialize`, `EncryptedDirectMessage#initialize` and `Filter#to_h`
39
66
 
@@ -115,6 +142,7 @@ principles of immutability and was a major source of internal complexity as I ne
115
142
 
116
143
  - Initial release
117
144
 
145
+ [0.7.0]: https://github.com/wilsonsilva/nostr/compare/v0.6.0...v0.7.0
118
146
  [0.6.0]: https://github.com/wilsonsilva/nostr/compare/v0.5.0...v0.6.0
119
147
  [0.5.0]: https://github.com/wilsonsilva/nostr/compare/v0.4.0...v0.5.0
120
148
  [0.4.0]: https://github.com/wilsonsilva/nostr/compare/v0.3.0...v0.4.0
data/README.md CHANGED
@@ -80,7 +80,7 @@ relay = Nostr::Relay.new(url: 'wss://nostr.wine', name: 'Wine')
80
80
  client.connect(relay)
81
81
 
82
82
  # Listen asynchronously for the connect event
83
- client.on :connect do
83
+ client.on :connect do |relay|
84
84
  # Send the event to the Relay
85
85
  client.publish(text_note_event)
86
86
 
@@ -118,6 +118,9 @@ end
118
118
  client.on :close do |code, reason|
119
119
  # You may attempt to reconnect to the relay here
120
120
  end
121
+
122
+ # This line keeps the background client from exiting immediately.
123
+ gets
121
124
  ```
122
125
 
123
126
  ## 📚 Documentation
@@ -0,0 +1,122 @@
1
+ # 3. Logging methods vs logger class
2
+
3
+ Date: 2024-03-19
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ I'm deciding between integrating logging directly into the main class or creating a dedicated logger class.
12
+
13
+ ### Option 1: Logging methods
14
+
15
+ The first approach weaves logging actions into the operational code, resulting in a tight coupling of functionality and
16
+ logging. Classes should be open for extension but closed for modification, and this strategy violates that principle.
17
+
18
+ ```ruby
19
+ class Client
20
+ def connect(relay)
21
+ execute_within_an_em_thread do
22
+ client = build_websocket_client(relay.url)
23
+ parent_to_child_channel.subscribe do |msg|
24
+ client.send(msg)
25
+ emit(:send, msg)
26
+ log_send(msg) # <------ new code
27
+ end
28
+
29
+ client.on :open do
30
+ child_to_parent_channel.push(type: :open, relay:)
31
+ log_connection_opened(relay) # <------ new code
32
+ end
33
+
34
+ client.on :message do |event|
35
+ child_to_parent_channel.push(type: :message, data: event.data)
36
+ log_message_received(event.data) # <------ new code
37
+ end
38
+
39
+ client.on :error do |event|
40
+ child_to_parent_channel.push(type: :error, message: event.message)
41
+ log_error(event.message) # <------ new code
42
+ end
43
+
44
+ client.on :close do |event|
45
+ child_to_parent_channel.push(type: :close, code: event.code, reason: event.reason)
46
+ log_connection_closed(event.code, event.reason) # <------ new code
47
+ end
48
+ end
49
+
50
+ # ------ new code below ------
51
+
52
+ def log_send(msg)
53
+ logger.info("Message sent: #{msg}")
54
+ end
55
+
56
+ def log_connection_opened(relay)
57
+ logger.info("Connection opened to #{relay.url}")
58
+ end
59
+
60
+ def log_message_received(data)
61
+ logger.info("Message received: #{data}")
62
+ end
63
+
64
+ def log_error(message)
65
+ logger.error("Error: #{message}")
66
+ end
67
+
68
+ def log_connection_closed(code, reason)
69
+ logger.info("Connection closed with code: #{code}, reason: #{reason}")
70
+ end
71
+ end
72
+ end
73
+ ```
74
+
75
+ ### Option 2: Logger class
76
+
77
+ The second strategy separates logging into its own class, promoting cleaner code and adherence to the Single
78
+ Responsibility Principle. Client already exposes events that can be tapped into, so the logger class can listen to these
79
+ events and log accordingly.
80
+
81
+ ```ruby
82
+ class ClientLogger
83
+ def attach_to(client)
84
+ logger_instance = self
85
+
86
+ client.on(:connect) { |relay| logger_instance.on_connect(relay) }
87
+ client.on(:message) { |message| logger_instance.on_message(message) }
88
+ client.on(:send) { |message| logger_instance.on_send(message) }
89
+ client.on(:error) { |message| logger_instance.on_error(message) }
90
+ client.on(:close) { |code, reason| logger_instance.on_close(code, reason) }
91
+ end
92
+
93
+ def on_connect(relay); end
94
+ def on_message(message); end
95
+ def on_send(message); end
96
+ def on_error(message); end
97
+ def on_close(code, reason); end
98
+ end
99
+
100
+ client = Nostr::Client.new
101
+ logger = Nostr::ClientLogger.new
102
+ logger.attach_to(client)
103
+ ```
104
+
105
+ This approach decouples logging from the main class, making it easier to maintain and extend the logging system without
106
+ affecting the core logic.
107
+
108
+ ## Decision
109
+
110
+ I've chosen the dedicated logger class route. This choice is driven by a desire for extensibility in the logging system.
111
+ With a separate logger, I can easily modify logging behavior—like changing formats, adjusting verbosity levels,
112
+ switching colors, or altering output destinations (files, networks, etc.) — without needing to rewrite any of the main
113
+ operational code.
114
+
115
+ ## Consequences
116
+
117
+ Adopting a dedicated logger class offers greater flexibility and simplifies maintenance, making it straightforward to
118
+ adjust how and what I log independently of the core logic. This separation of concerns means that any future changes
119
+ to logging preferences or requirements can be implemented quickly and without risk to the main class's functionality.
120
+ However, it's important to manage the integration carefully to avoid introducing complexity, such as handling
121
+ dependencies and ensuring seamless communication between the main operations and the logging system.
122
+
@@ -0,0 +1,66 @@
1
+ # 3. Default Logging Activation
2
+
3
+ Date: 2024-03-19
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ Logging provides visibility into the gem's behavior and helps to diagnose issues. The decision centered on whether
12
+ to enable logging by default or require manual activation.
13
+
14
+ ### Option 1: On-demand Logging
15
+
16
+ ```ruby
17
+ client = Nostr::Client.new
18
+ logger = Nostr::ClientLogger.new
19
+ logger.attach_to(client)
20
+ ```
21
+
22
+ #### Advantages:
23
+
24
+ - Offers users flexibility and control over logging.
25
+ - Conserves resources by logging only when needed.
26
+
27
+ #### Disadvantages:
28
+
29
+ - Potential to miss critical logs if not enabled.
30
+ - Requires users to read and understand documentation to enable logging.
31
+ - Requires users to manually activate and configure logging.
32
+
33
+ ### Option 2: Automatic Logging
34
+
35
+ ```ruby
36
+ class Client
37
+ def initialize(logger: ClientLogger.new)
38
+ @logger = logger
39
+ logger&.attach_to(self)
40
+ end
41
+ end
42
+
43
+ client = Nostr::Client.new
44
+ ```
45
+
46
+ #### Advantages:
47
+
48
+ - Ensures comprehensive logging without user intervention.
49
+ - Simplifies debugging and monitoring.
50
+ - Balances logging detail with performance impact.
51
+
52
+ #### Disadvantages:
53
+
54
+ - Needs additional steps to disable logging.
55
+
56
+ ## Decision
57
+
58
+ Logging will be enabled by default. Users can disable logging by passing a `nil` logger to the client:
59
+
60
+ ```ruby
61
+ client = Nostr::Client.new(logger: nil)
62
+ ```
63
+
64
+ ## Consequences
65
+
66
+ Enabling logging by default favors ease of use and simplifies the developer's experience.
@@ -0,0 +1,32 @@
1
+ # 3. Logger Types
2
+
3
+ Date: 2024-04-01
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ In developing the `Nostr::Client` logging mechanism, I identified a need to cater to multiple development environments
12
+ and developer preferences. The consideration was whether to implement a singular logger type or to introduce
13
+ multiple, specialized loggers. The alternatives were:
14
+
15
+ - A single `ClientLogger` providing a one-size-fits-all solution.
16
+ - Multiple logger types:
17
+ - `Nostr::Client::Logger`: The base class for logging, providing core functionalities.
18
+ - `Nostr::Client::ColorLogger`: An extension of the base logger, introducing color-coded outputs for enhanced readability in environments that support ANSI colors.
19
+ - `Nostr::Client::PlainLogger`: A variation that produces logs without color coding, suitable for environments lacking color support or for users preferring plain text.
20
+
21
+ ## Decision
22
+
23
+ I decided to implement the latter option: three specific kinds of loggers (`Nostr::Client::Logger`,
24
+ `Nostr::Client::ColorLogger`, and `Nostr::Client::PlainLogger`). This approach is intended to offer flexibility and
25
+ cater to the varied preferences and requirements of our users, recognizing the diverse environments in which our
26
+ library might be used.
27
+
28
+ ## Consequences
29
+
30
+ - `Developer Choice`: Developers gain the ability to select the one that best matches their needs and environmental constraints, thereby enhancing the library's usability.
31
+ - `Code Complexity`: While introducing multiple logger types increases the library's code complexity, this is offset by the significant gain in flexibility and user satisfaction.
32
+ - `Broad Compatibility`: This decision ensures that the logging mechanism is adaptable to a wide range of operational environments, enhancing the library's overall robustness and accessibility.
@@ -78,6 +78,7 @@ export default defineConfig(withMermaid({
78
78
  text: 'Common use cases',
79
79
  collapsed: false,
80
80
  items: [
81
+ { text: 'Logging and debugging', link: '/common-use-cases/logging-and-debugging' },
81
82
  { text: 'Bech32 enc/decoding (NIP-19)', link: '/common-use-cases/bech32-encoding-and-decoding-(NIP-19)' },
82
83
  { text: 'Signing/verifying messages', link: '/common-use-cases/signing-and-verifying-messages' },
83
84
  { text: 'Signing/verifying events', link: '/common-use-cases/signing-and-verifying-events' },
data/docs/bun.lockb CHANGED
Binary file
@@ -0,0 +1,60 @@
1
+ # Logging and debugging
2
+
3
+ The `Nostr::Client` class provides built-in logging functionality to help you debug and monitor client interactions with
4
+ relays. By default, the client uses the `ColorLogger`, which logs events in color. However, you can customize the
5
+ logging behavior or disable it entirely.
6
+
7
+ ## Disabling logging
8
+
9
+ To instantiate a client without any logging, simply pass `logger: nil` when creating the client instance:
10
+
11
+ ```ruby
12
+ client = Nostr::Client.new(logger: nil)
13
+ ```
14
+
15
+ This will disable all logging for the client.
16
+
17
+ ## Formatting the logging
18
+
19
+ The `Nostr::Client::Logger` class is the base class for logging functionality. It defines the following methods for
20
+ logging different events:
21
+
22
+ - `on_connect(relay)`: Logs when the client connects to a relay.
23
+ - `on_message(message)`: Logs a message received from the relay.
24
+ - `on_send(message)`: Logs a message sent to the relay.
25
+ - `on_error(message)`: Logs an error message.
26
+ - `on_close(code, reason)`: Logs when the connection with a relay is closed.
27
+
28
+ You can create your own logger by subclassing `Nostr::Client::Logger` and overriding these methods to customize the
29
+ logging format.
30
+
31
+ The `Nostr::Client::ColorLogger` is a built-in logger that logs events in color. It uses ANSI escape codes to add color
32
+ to the log output. Here's an example of how the ColorLogger formats the log messages:
33
+
34
+ - Connection: `"\u001b[32m\u001b[1mConnected to the relay\u001b[22m #{relay.name} (#{relay.url})\u001b[0m"`
35
+ - Message received: `"\u001b[32m\u001b[1m◄-\u001b[0m #{message}"`
36
+ - Message sent: `"\u001b[32m\u001b[1m-►\u001b[0m #{message}"`
37
+ - Error: `"\u001b[31m\u001b[1mError: \u001b[22m#{message}\u001b[0m"`
38
+ - Connection closed: `"\u001b[31m\u001b[1mConnection closed: \u001b[22m#{reason} (##{code})\u001b[0m"`
39
+
40
+ ## Plain text logging
41
+
42
+ If you prefer plain text logging without colors, you can use the `Nostr::Client::PlainLogger`. This logger formats the
43
+ log messages in a simple, readable format without any ANSI escape codes.
44
+
45
+ To use the `PlainLogger`, pass it as the `logger` option when creating the client instance:
46
+
47
+ ```ruby
48
+ client = Nostr::Client.new(logger: Nostr::Client::PlainLogger.new)
49
+ ```
50
+
51
+ The `PlainLogger` formats the log messages as follows:
52
+
53
+ - Connection: `"Connected to the relay #{relay.name} (#{relay.url})"`
54
+ - Message received: `"◄- #{message}"`
55
+ - Message sent: `"-► #{message}"`
56
+ - Error: `"Error: #{message}"`
57
+ - Connection closed: `"Connection closed: #{reason} (##{code})"`
58
+
59
+ By using the appropriate logger or creating your own custom logger, you can effectively debug and monitor your Nostr
60
+ client's interactions with relays.
data/docs/core/client.md CHANGED
@@ -28,7 +28,7 @@ The `:connect` event is fired when a connection with a WebSocket is opened. You
28
28
  client = Nostr::Client.new
29
29
  relay = Nostr::Relay.new(url: 'wss://relay.damus.io', name: 'Damus')
30
30
 
31
- client.on :connect do
31
+ client.on :connect do |relay|
32
32
  # When this block executes, you're connected to the relay
33
33
  end
34
34
 
@@ -165,6 +165,9 @@ end
165
165
  client.on :close do |code, reason|
166
166
  # You may attempt to reconnect to the relay here
167
167
  end
168
+
169
+ # This line keeps the background client from exiting immediately.
170
+ gets
168
171
  ```
169
172
 
170
173
  Beyond what's covered here, the Nostr protocol and this gem boast a wealth of additional functionalities. For an
data/docs/package.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "docs:preview": "vitepress preview"
6
6
  },
7
7
  "devDependencies": {
8
- "mermaid": "^10.6.1",
9
- "vitepress": "^1.0.0-rc.25",
10
- "vitepress-plugin-mermaid": "^2.0.15"
8
+ "mermaid": "^10.9.0",
9
+ "vitepress": "^1.1.0",
10
+ "vitepress-plugin-mermaid": "^2.0.16"
11
11
  }
12
12
  }
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nostr
4
+ class Client
5
+ # Logs connection events, messages sent and received, errors, and connection closures in color.
6
+ class ColorLogger < Logger
7
+ # Logs connection to a relay
8
+ #
9
+ # @api private
10
+ #
11
+ # @param [Nostr::Relay] relay The relay the client connected to.
12
+ #
13
+ # @return [void]
14
+ #
15
+ def on_connect(relay)
16
+ puts "\u001b[32m\u001b[1mConnected to the relay\u001b[22m #{relay.name} (#{relay.url})\u001b[0m"
17
+ end
18
+
19
+ # Logs a message received from the relay
20
+ #
21
+ # @api private
22
+ #
23
+ # @param [String] message The message received.
24
+ #
25
+ # @return [void]
26
+ #
27
+ def on_message(message)
28
+ puts "\u001b[32m\u001b[1m◄-\u001b[0m #{message}"
29
+ end
30
+
31
+ # Logs a message sent to the relay
32
+ #
33
+ # @api private
34
+ #
35
+ # @param [String] message The message sent.
36
+ #
37
+ # @return [void]
38
+ #
39
+ def on_send(message)
40
+ puts "\u001b[32m\u001b[1m-►\u001b[0m #{message}"
41
+ end
42
+
43
+ # Logs an error message
44
+ #
45
+ # @api private
46
+ #
47
+ # @param [String] message The error message.
48
+ #
49
+ # @return [void]
50
+ #
51
+ def on_error(message)
52
+ puts "\u001b[31m\u001b[1mError: \u001b[22m#{message}\u001b[0m"
53
+ end
54
+
55
+ # Logs a closure of connection with a relay
56
+ #
57
+ # @api private
58
+ #
59
+ # @param [String] code The closure code.
60
+ # @param [String] reason The reason for the closure.
61
+ #
62
+ # @return [void]
63
+ #
64
+ def on_close(code, reason)
65
+ puts "\u001b[31m\u001b[1mConnection closed: \u001b[22m#{reason} (##{code})\u001b[0m"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nostr
4
+ class Client
5
+ # Logs connection events, messages sent and received, errors, and connection closures.
6
+ class Logger
7
+ # Attaches event handlers to the specified Nostr client for logging purposes
8
+ #
9
+ # @api public
10
+ #
11
+ # @example Attaching the logger to a client
12
+ # client = Nostr::Client.new
13
+ # logger = Nostr::Client::Logger.new
14
+ # logger.attach_to(client)
15
+ #
16
+ # # Now, actions like connecting, sending messages, receiving messages,
17
+ # # errors, and closing the connection will be logged to the console.
18
+ #
19
+ # @param [Nostr::Client] client The client to attach logging functionality to.
20
+ #
21
+ # @return [void]
22
+ #
23
+ def attach_to(client)
24
+ logger_instance = self
25
+
26
+ client.on(:connect) { |relay| logger_instance.on_connect(relay) }
27
+ client.on(:message) { |message| logger_instance.on_message(message) }
28
+ client.on(:send) { |message| logger_instance.on_send(message) }
29
+ client.on(:error) { |message| logger_instance.on_error(message) }
30
+ client.on(:close) { |code, reason| logger_instance.on_close(code, reason) }
31
+ end
32
+
33
+ # Logs connection to a relay
34
+ #
35
+ # @api private
36
+ #
37
+ # @param [Nostr::Relay] relay The relay the client connected to.
38
+ #
39
+ # @return [void]
40
+ #
41
+ def on_connect(relay); end
42
+
43
+ # Logs a message received from the relay
44
+ #
45
+ # @api private
46
+ #
47
+ # @param [String] message The message received.
48
+ #
49
+ # @return [void]
50
+ #
51
+ def on_message(message); end
52
+
53
+ # Logs a message sent to the relay
54
+ #
55
+ # @api private
56
+ #
57
+ # @param [String] message The message sent.
58
+ #
59
+ # @return [void]
60
+ #
61
+ def on_send(message); end
62
+
63
+ # Logs an error message
64
+ #
65
+ # @api private
66
+ #
67
+ # @param [String] message The error message.
68
+ #
69
+ # @return [void]
70
+ #
71
+ def on_error(message); end
72
+
73
+ # Logs a closure of connection with a relay
74
+ #
75
+ # @api private
76
+ #
77
+ # @param [String] code The closure code.
78
+ # @param [String] reason The reason for the closure.
79
+ #
80
+ # @return [void]
81
+ #
82
+ def on_close(code, reason); end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nostr
4
+ class Client
5
+ # Logs connection events, messages sent and received, errors, and connection closures.
6
+ class PlainLogger < Logger
7
+ # Logs connection to a relay
8
+ #
9
+ # @api private
10
+ #
11
+ # @param [Nostr::Relay] relay The relay the client connected to.
12
+ #
13
+ # @return [void]
14
+ #
15
+ def on_connect(relay)
16
+ puts "Connected to the relay #{relay.name} (#{relay.url})"
17
+ end
18
+
19
+ # Logs a message received from the relay
20
+ #
21
+ # @api private
22
+ #
23
+ # @param [String] message The message received.
24
+ #
25
+ # @return [void]
26
+ #
27
+ def on_message(message)
28
+ puts "◄- #{message}"
29
+ end
30
+
31
+ # Logs a message sent to the relay
32
+ #
33
+ # @api private
34
+ #
35
+ # @param [String] message The message sent.
36
+ #
37
+ # @return [void]
38
+ #
39
+ def on_send(message)
40
+ puts "-► #{message}"
41
+ end
42
+
43
+ # Logs an error message
44
+ #
45
+ # @api private
46
+ #
47
+ # @param [String] message The error message.
48
+ #
49
+ # @return [void]
50
+ #
51
+ def on_error(message)
52
+ puts "Error: #{message}"
53
+ end
54
+
55
+ # Logs a closure of connection with a relay
56
+ #
57
+ # @api private
58
+ #
59
+ # @param [String] code The closure code.
60
+ # @param [String] reason The reason for the closure.
61
+ #
62
+ # @return [void]
63
+ #
64
+ def on_close(code, reason)
65
+ puts "Connection closed: #{reason} (##{code})"
66
+ end
67
+ end
68
+ end
69
+ end
data/lib/nostr/client.rb CHANGED
@@ -18,11 +18,19 @@ module Nostr
18
18
  # @api public
19
19
  #
20
20
  # @example Instantiating a client that logs all the events it sends and receives
21
- # client = Nostr::Client.new(debug: true)
21
+ # client = Nostr::Client.new
22
22
  #
23
- def initialize
23
+ # @example Instantiating a client with no logging
24
+ # client = Nostr::Client.new(logger: nil)
25
+ #
26
+ # @example Instantiating a client with your own logger
27
+ # client = Nostr::Client.new(logger: YourLogger.new)
28
+ #
29
+ def initialize(logger: ColorLogger.new)
24
30
  @subscriptions = {}
31
+ @logger = logger
25
32
 
33
+ logger&.attach_to(self)
26
34
  initialize_channels
27
35
  end
28
36
 
@@ -40,11 +48,11 @@ module Nostr
40
48
  #
41
49
  def connect(relay)
42
50
  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) }
51
+ client = build_websocket_client(relay.url)
52
+ parent_to_child_channel.subscribe { |msg| client.send(msg) && emit(:send, msg) }
45
53
 
46
54
  client.on :open do
47
- child_to_parent_channel.push(type: :open)
55
+ child_to_parent_channel.push(type: :open, relay:)
48
56
  end
49
57
 
50
58
  client.on :message do |event|
@@ -122,6 +130,14 @@ module Nostr
122
130
 
123
131
  private
124
132
 
133
+ # The logger that prints all the events that the client sends and receives
134
+ #
135
+ # @api private
136
+ #
137
+ # @return [ClientLogger]
138
+ #
139
+ attr_reader :logger
140
+
125
141
  # The subscriptions that the client has created
126
142
  #
127
143
  # @api private
@@ -167,11 +183,21 @@ module Nostr
167
183
  @child_to_parent_channel = EventMachine::Channel.new
168
184
 
169
185
  child_to_parent_channel.subscribe do |msg|
170
- emit :connect if msg[:type] == :open
186
+ emit :connect, msg[:relay] if msg[:type] == :open
171
187
  emit :message, msg[:data] if msg[:type] == :message
172
188
  emit :error, msg[:message] if msg[:type] == :error
173
189
  emit :close, msg[:code], msg[:reason] if msg[:type] == :close
174
190
  end
175
191
  end
192
+
193
+ # Builds a websocket client
194
+ #
195
+ # @api private
196
+ #
197
+ # @return [Faye::WebSocket::Client]
198
+ #
199
+ def build_websocket_client(relay_url)
200
+ Faye::WebSocket::Client.new(relay_url, [], { tls: { verify_peer: false } })
201
+ end
176
202
  end
177
203
  end
@@ -11,6 +11,6 @@ module Nostr
11
11
  # @example
12
12
  # InvalidSignatureTypeError.new
13
13
  #
14
- def initialize = super('Invalid signature type')
14
+ def initialize = super('Invalid signature type. It must be a string with lowercase hexadecimal characters.')
15
15
  end
16
16
  end
data/lib/nostr/event.rb CHANGED
@@ -203,8 +203,7 @@ module Nostr
203
203
  def verify_signature
204
204
  crypto = Crypto.new
205
205
 
206
- return false if id.nil? || pubkey.nil?
207
- return false if sig.nil? # FIXME: See https://github.com/soutaro/steep/issues/1079
206
+ return false if id.nil? || pubkey.nil? || sig.nil?
208
207
 
209
208
  crypto.valid_sig?(id, pubkey, sig)
210
209
  end
data/lib/nostr/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Nostr
4
4
  # The version of the gem
5
- VERSION = '0.6.0'
5
+ VERSION = '0.7.0'
6
6
  end
data/lib/nostr.rb CHANGED
@@ -16,6 +16,8 @@ require_relative 'nostr/signature'
16
16
  require_relative 'nostr/event'
17
17
  require_relative 'nostr/events/encrypted_direct_message'
18
18
  require_relative 'nostr/client'
19
+ require_relative 'nostr/client/logger'
20
+ require_relative 'nostr/client/color_logger'
19
21
  require_relative 'nostr/user'
20
22
  require_relative 'nostr/key'
21
23
  require_relative 'nostr/private_key'
data/nostr.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_dependency 'ecdsa', '~> 1.2'
37
37
  spec.add_dependency 'event_emitter', '~> 0.2'
38
38
  spec.add_dependency 'faye-websocket', '~> 0.11'
39
- spec.add_dependency 'json', '~> 2.6'
39
+ spec.add_dependency 'json', '~> 2.7'
40
40
 
41
41
  spec.add_development_dependency 'bundler-audit', '~> 0.9'
42
42
  spec.add_development_dependency 'dotenv', '~> 3.1'
@@ -54,10 +54,10 @@ Gem::Specification.new do |spec|
54
54
  spec.add_development_dependency 'rspec', '~> 3.13'
55
55
  spec.add_development_dependency 'rubocop', '~> 1.62'
56
56
  spec.add_development_dependency 'rubocop-rake', '~> 0.6'
57
- spec.add_development_dependency 'rubocop-rspec', '2.27'
57
+ spec.add_development_dependency 'rubocop-rspec', '2.29'
58
58
  spec.add_development_dependency 'simplecov', '= 0.17'
59
59
  spec.add_development_dependency 'simplecov-console', '~> 0.9'
60
- spec.add_development_dependency 'steep', '~> 1.6'
60
+ spec.add_development_dependency 'steep', '~> 1.7.dev3'
61
61
  spec.add_development_dependency 'typeprof', '~> 0.21'
62
62
  spec.add_development_dependency 'yard', '~> 0.9'
63
63
  spec.add_development_dependency 'yard-junk', '~> 0.0.9'
@@ -0,0 +1,6 @@
1
+ module Nostr
2
+ class Client
3
+ class ColorLogger < Logger
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ module Nostr
2
+ class Client
3
+ class Logger
4
+ def attach_to: (Client client) -> void
5
+ def on_connect: (Relay relay) -> void
6
+ def on_message: (String message) -> void
7
+ def on_send: (String message) -> void
8
+ def on_error: (String message) -> void
9
+ def on_close: (String code, String reason) -> void
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ module Nostr
2
+ class Client
3
+ class PlainLogger < Logger
4
+ end
5
+ end
6
+ end
data/sig/nostr/client.rbs CHANGED
@@ -10,11 +10,13 @@ module Nostr
10
10
 
11
11
  private
12
12
 
13
+ attr_reader logger: Logger
13
14
  attr_reader subscriptions: Hash[String, Subscription]
14
15
  attr_reader parent_to_child_channel: EventMachine::Channel
15
16
  attr_reader child_to_parent_channel: EventMachine::Channel
16
17
 
17
18
  def execute_within_an_em_thread: { -> void } -> Thread
18
19
  def initialize_channels: -> void
20
+ def build_websocket_client: (String relay_name) -> Faye::WebSocket::Client
19
21
  end
20
22
  end
@@ -1,16 +1,14 @@
1
1
  # Added only to satisfy the Steep requirements. Not 100% reliable.
2
2
  module EventEmitter
3
- interface _Event
4
- def data: -> String
5
- def message: -> String
6
- def code: -> Integer
7
- def reason: -> String
8
- end
3
+ def self.included: (Module) -> void
4
+ def self.apply: (untyped) -> void
9
5
 
10
- def add_listener: (Symbol event_name) { (_Event event) -> void } -> void
6
+ def __events: () -> Array[untyped]
7
+
8
+ def add_listener: (Symbol | String type, ?Hash[untyped, untyped] params) { (*untyped) -> void } -> Integer
11
9
  alias on add_listener
10
+ alias once add_listener
12
11
 
13
- def remove_listener: (untyped id_or_type) -> Array[untyped]?
14
- def emit: (Symbol `type`, *untyped data) -> Array[untyped]
15
- def once: (Symbol `type`) -> Integer
12
+ def remove_listener: (Integer | Symbol | String id_or_type) -> void
13
+ def emit: (Symbol | String type, *untyped data) -> void
16
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nostr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wilson Silva
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-15 00:00:00.000000000 Z
11
+ date: 2024-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bech32
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '2.6'
89
+ version: '2.7'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '2.6'
96
+ version: '2.7'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: bundler-audit
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -324,14 +324,14 @@ dependencies:
324
324
  requirements:
325
325
  - - '='
326
326
  - !ruby/object:Gem::Version
327
- version: '2.27'
327
+ version: '2.29'
328
328
  type: :development
329
329
  prerelease: false
330
330
  version_requirements: !ruby/object:Gem::Requirement
331
331
  requirements:
332
332
  - - '='
333
333
  - !ruby/object:Gem::Version
334
- version: '2.27'
334
+ version: '2.29'
335
335
  - !ruby/object:Gem::Dependency
336
336
  name: simplecov
337
337
  requirement: !ruby/object:Gem::Requirement
@@ -366,14 +366,14 @@ dependencies:
366
366
  requirements:
367
367
  - - "~>"
368
368
  - !ruby/object:Gem::Version
369
- version: '1.6'
369
+ version: 1.7.dev3
370
370
  type: :development
371
371
  prerelease: false
372
372
  version_requirements: !ruby/object:Gem::Requirement
373
373
  requirements:
374
374
  - - "~>"
375
375
  - !ruby/object:Gem::Version
376
- version: '1.6'
376
+ version: 1.7.dev3
377
377
  - !ruby/object:Gem::Dependency
378
378
  name: typeprof
379
379
  requirement: !ruby/object:Gem::Requirement
@@ -455,12 +455,16 @@ files:
455
455
  - Steepfile
456
456
  - adr/0001-record-architecture-decisions.md
457
457
  - adr/0002-introduction-of-signature-class.md
458
+ - adr/0003-logging-methods-vs-logger-class.md
459
+ - adr/0004-default-logging-activation.md
460
+ - adr/0005-logger-types.md
458
461
  - docs/.gitignore
459
462
  - docs/.vitepress/config.mjs
460
463
  - docs/README.md
461
464
  - docs/api-examples.md
462
465
  - docs/bun.lockb
463
466
  - docs/common-use-cases/bech32-encoding-and-decoding-(NIP-19).md
467
+ - docs/common-use-cases/logging-and-debugging.md
464
468
  - docs/common-use-cases/signing-and-verifying-events.md
465
469
  - docs/common-use-cases/signing-and-verifying-messages.md
466
470
  - docs/core/client.md
@@ -488,6 +492,9 @@ files:
488
492
  - lib/nostr.rb
489
493
  - lib/nostr/bech32.rb
490
494
  - lib/nostr/client.rb
495
+ - lib/nostr/client/color_logger.rb
496
+ - lib/nostr/client/logger.rb
497
+ - lib/nostr/client/plain_logger.rb
491
498
  - lib/nostr/client_message_type.rb
492
499
  - lib/nostr/crypto.rb
493
500
  - lib/nostr/errors.rb
@@ -520,6 +527,9 @@ files:
520
527
  - sig/nostr.rbs
521
528
  - sig/nostr/bech32.rbs
522
529
  - sig/nostr/client.rbs
530
+ - sig/nostr/client/color_logger.rbs
531
+ - sig/nostr/client/logger.rbs
532
+ - sig/nostr/client/plain_logger.rbs
523
533
  - sig/nostr/client_message_type.rbs
524
534
  - sig/nostr/crypto.rbs
525
535
  - sig/nostr/errors/error.rbs