nostr 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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