diff --git a/app/services/nostr_manager/discover_user_relays.rb b/app/services/nostr_manager/discover_user_relays.rb new file mode 100644 index 0000000..942c471 --- /dev/null +++ b/app/services/nostr_manager/discover_user_relays.rb @@ -0,0 +1,33 @@ +module NostrManager + class DiscoverUserRelays < NostrManagerService + def initialize(pubkey:) + @pubkey = pubkey + @relays = Setting.nostr_discovery_relays + # @relays = %w[ + # wss://nostr.kosmos.org + # wss://purplepag.es + # wss://relay.nostr.band + # wss://njump.me + # ] + end + + def call + nip65_events = [] + user_relays = [] + filter = Nostr::Filter.new( + authors: [@pubkey], + kinds: [10002], + limit: 1, + ) + + @relays.each do |url| + event = NostrManager::FetchLatestEvent.call(filter: filter, relay_url: url) + if event.present? && user_relays.detect { |r| r[:id] == event["id"] }.nil? + user_relays << { id: event["id"], created_at: event["created_at"] } + end + end + + puts user_relays.inspect + end + end +end diff --git a/app/services/nostr_manager/fetch_latest_event.rb b/app/services/nostr_manager/fetch_latest_event.rb new file mode 100644 index 0000000..ad47174 --- /dev/null +++ b/app/services/nostr_manager/fetch_latest_event.rb @@ -0,0 +1,59 @@ +module NostrManager + class FetchLatestEvent < NostrManagerService + TIMEOUT = 1 + + def initialize(filter:, relay_url:) + @filter = filter + @relay = new_relay(relay_url) + @client = Nostr::Client.new + end + + def call + filter, client, relay = @filter, @client, @relay + latest_event = nil + mutex = Mutex.new + received_event = ConditionVariable.new + log_prefix = "[nostr][#{@relay.name}]" + + thread = Thread.new do + client.on :connect do + client.subscribe(filter: filter) + end + + client.on :error do |e| + Rails.logger.info "#{log_prefix} Error: #{e}" + Thread.current.exit + end + + client.on :message do |m| + msg = JSON.parse(m) rescue nil + if msg && msg[0] == "EVENT" && msg[2] + puts "#{log_prefix} Event received: #{msg[2]["id"]}" + mutex.synchronize do + latest_event = msg[2] + received_event.signal + end + elsif msg && msg[0] == "EOSE" + Thread.current.exit + end + end + + client.connect relay + end + + begin + Timeout.timeout(TIMEOUT) do + mutex.synchronize do + received_event.wait(mutex) if latest_event.nil? + end + end + rescue Timeout::Error + puts "#{log_prefix} Timeout: No event received within #{TIMEOUT} seconds" + ensure + thread.exit + end + + latest_event + end + end +end diff --git a/app/services/nostr_manager_service.rb b/app/services/nostr_manager_service.rb index 581ab10..7a85dc6 100644 --- a/app/services/nostr_manager_service.rb +++ b/app/services/nostr_manager_service.rb @@ -19,4 +19,8 @@ class NostrManagerService < ApplicationService def site_user Nostr::User.new(keypair: site_keypair) end + + def new_relay(url) + Nostr::Relay.new(url: url, name: URI.parse(url).host) + end end