Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

dtonon/nostr-ruby

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nostr Ruby

A ruby library to interact with the Nostr Protocol.

Warning

This version in work in progress and breaks the v0.2.0 API

Installation

Add this line to your application's Gemfile:

# Gemfile
gem 'nostr_ruby'

And then execute:

$ bundle

Or install it yourself as:

$ gem install nostr_ruby

Usage

Manage keys

require "nostr_ruby"

sk = Nostr::Key.generate_private_key
# => "8090fb3fe26e27d539ee349d70890d338c5e2e8b459e04c8e97658f03d2f9f33"

pk = Nostr::Key.get_public_key(sk)
# => "e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6"

Decode entities

puplic_key = Nostr::Bech32.decode(npub)
# => {:hrp=>"npub", :data=>"e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6"}

nprofile_data = Nostr::Bech32.decode("nprofile1qqs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgprpmhxue69uhkgar0dehkutnwdaehgu339e3k7mf06ras84")
# => {:hrp=>"nprofile", :data=>{:pubkey=>["7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805"], :relay=>["wss://dtonon.nostr1.com/"]}}

note_data = Nostr::Bech32.decode("note1xzce08egncw3mcm8l8edas6rrhgfj9l5uwwv2hz03zym0m9eg5hsxuyajp")
# => {:hrp=>"note", :data=>"30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f"}

nevent_data = Nostr::Bech32.decode("nevent1qqsrpvvhnu5fu8gaudnlnuk7cdp3m5yezl6w88x9t38c3zdhaju52tcpzpmhxue69uhkztnwdaejumr0dshsz9nhwden5te0vfjhvmewdehhxarjxyhxxmmd9uq3wamnwvaz7tmzd96xxmmfdejhytnnda3kjctv9ulrdeva")
# => {:hrp=>"nevent", :data=> {:id=>["30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f"], :relay=>["wss://a.nos.lol/", "wss://bevo.nostr1.com/", "wss://bitcoiner.social/"]}}

naddr_data = Nostr::Bech32.decode("naddr1qvzqqqr4gupzq77777lz9hvwt86xqrsyf2jn588ewk5aclf8mavr80rhmduy5kq9qqdkc6t5w3kx2ttvdamx2ttxdaez6mr0denj6en0wfkkzaqxq5r99")
# => => {:hrp=>"naddr", :data=>{:kind=>[30023], :author=>["7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805"], :identifier=>["little-love-for-long-format"]}}

Encode entities

nsec = Nostr::Bech32.encode_nsec(sk)
# => "nsec1szg0k0lzdcna2w0wxjwhpzgdxwx9ut5tgk0qfj8fwev0q0f0nuessml5ur"

npub = Nostr::Bech32.encode_npub(pk)
# => "npub1ul0dn02zulr5lnryvktzhyvm0m7d2a62c6l29tntsxev42w56tnqksrtfu"

nprofile = Nostr::Bech32.encode_nprofile(
  pubkey: "7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805",
  relays: ["wss://dtonon.nostr1.com"],
)
# => "nprofile1qqs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgpzamhxue69uhkgar0dehkutnwdaehgu339e3k7mg60me8x"

note = Nostr::Bech32.encode_note("30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f")
# => "note1xzce08egncw3mcm8l8edas6rrhgfj9l5uwwv2hz03zym0m9eg5hsxuyajp"

nevent = Nostr::Bech32.encode_nevent(
  id: "30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f",
  relays: ["wss://nos.lol"],
)
# => "nevent1qqsrpvvhnu5fu8gaudnlnuk7cdp3m5yezl6w88x9t38c3zdhaju52tcpp4mhxue69uhkummn9ekx7mqrqsqqqqqpux7e9q"

naddr = Nostr::Bech32.encode_naddr(
  author: "7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805",
  identifier: "little-love-for-long-format",
  kind: 30023
)
# => "naddr1qgs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgrqsqqqa28qqdkc6t5w3kx2ttvdamx2ttxdaez6mr0denj6en0wfkkzaqn2tjdj"

Initialize Client

require "nostr_ruby"

# Detailed version
s = Nostr::Signer.new(private_key: Nostr::Key.generate_private_key)
c = Nostr::Client.new(signer: s)

# Compact version, under the hood Client creates a Signer
c = Nostr::Client.new(private_key: Nostr::Key.generate_private_key)

c.private_key
# => "7402b4b1ee09fb37b64ec2a958f1b7815d904c6dd44227bdef7912ef201af97d"

c.public_key
# => "a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466"

c.nsec
# => "nsec1wsptfv0wp8an0djwc2543udhs9weqnrd63pz00000yfw7gq6l97snckpdq"

c.npub
# => "npub15x0nc94kapta9dnncelw52f5x87pwky42y729a585ut322ja53nq72yrcr"

Create, sign and send an event

# Initialize a client, under the hood Client creates a Signer
c = Nostr::Client.new(
  private_key: "7402b4b1ee09fb37b64ec2a958f1b7815d904c6dd44227bdef7912ef201af97d",
  relay: "wss://nos.lol"
)

# Initialize an event
e = Nostr::Event.new(
  kind: ...,
  pubkey: ...,
  created_at: ...,
  tags: ...,
  content: ...,
  pow: ...,
  delegation: ...,
)

# Sign the event
e = c.sign(e)

# - - - - - - - - - - - - - -
# Full async mode

# Set the open callback
c.on :connect do |event|
  puts 'Publish event...'
end

# Set the response callback
c.on :ok do |event|
  puts "Event id: #{event.id}"
  puts "Accepted: #{event.success}"
  puts "Message: #{event.message}"
end

# Connect and send the event
c.connect
c.publish(e)
# Do more things
c.close

# - - - - - - - - - - - - - -
# Compact sync mode

c.connect
c.publish_and_wait(e)
c.close

Set the profile

metadata = {
  name: "Mr Robot",
  about: "I walk around the city",
  picture: "https://upload.wikimedia.org/wikipedia/commons/3/35/Mr_robot_photo.jpg",
  nip05: "mrrobot@mrrobot.com"
}

e = Nostr::Event.new(
  kind: Nostr::Kind::METADATA,
  pubkey: c.public_key,
  content: metadata.to_json,
)

Post a note

e = Nostr::Event.new(
  kind: Nostr::Kind::SHORT_NOTE,
  pubkey: c.public_key,
  content: "Hello Nostr!",
)

Share a contact list

contact_list = [
  ["54399b6d8200813bfc53177ad4f13d6ab712b6b23f91aefbf5da45aeb5c96b08", "wss://alicerelay.com/", "alice"],
  ["850708b7099215bf9a1356d242c2354939e9a844c1359d3b5209592a0b420452", "wss://bobrelay.com/nostr", "bob"],
  ["f7f4b0072368460a09138bf3966fb1c59d0bdadfc3aff4e59e6896194594a82a", "ws://carolrelay.com/ws", "carol"]
]

e = Nostr::Event.new(
  kind: Nostr::Kind::CONTACT_LIST,
  pubkey: c.public_key,
  tags: contact_list.map { |c| ['p'] + c },
)

Delete an event

event_to_delete = "b91b3fb40128112c38dc54168b9f601c22bf8fcae6e70bb2a5f53e7f3ae44388"
e = Nostr::Event.new(
  kind: Nostr::Kind::DELETION,
  pubkey: c.public_key,
  tags: [["e", event_to_delete]],
)

React to an event

e = Nostr::Event.new(
  kind: Nostr::Kind::REACTION,
  pubkey: c.public_key,
  content: "+",
  tags: [["e", target_event]],
)

# You can also use emoji
e2 = Nostr::Event.new(
  kind: Nostr::Kind::REACTION,
  pubkey: c.public_key,
  content: "🔥",
  tags: [["e", target_event]],
)

Create events with a PoW difficulty

# Just add the `pow` argument 
e = Nostr::Event.new(
  kind: Nostr::Kind::SHORT_NOTE,
  pubkey: c.public_key,
  content: "Hello Nostr!",
  pow: 15,
)

Create an event with a NIP-26 delegation

delegator = Nostr::Client.new(private_key: delegator_key)

delegatee = "b1d8dfd69fe8795042dbbc4d3f85938a01d4740c54d2daf11088c75c50ff19d9"
conditions = "kind=1&created_at>#{Time.now.to_i}&created_at<#{(Time.now + 60*60).to_i}"
delegation_tag = delegator.generate_delegation_tag(
  to: delegatee,
  conditions: conditions
)

# The `delegation_tag` is given to the delegatee so it can use it

delegatee = Nostr::Client.new(private_key: delegatee_key)

e = Nostr::Event.new(
  kind: Nostr::Kind::SHORT_NOTE,
  pubkey: delegatee.public_key,
  content: "Hello Nostr!",
  delegation: delegation_tag,
)

delegatee.sign(e)

Send a direct message

Warning: This uses NIP-04, that will be deprecated in favor of NIP-17

e = Nostr::Event.new(
  kind: Nostr::Kind::DIRECT_MESSAGE,
  pubkey: c.public_key,
  tags: [["p", Nostr::Bech32.decode(recipient)[:data]]],
  content: "Hello Alice!"
)

Decrypt a direct message

Warning: This uses NIP-04, that will be deprecated in favor of NIP-17

payload = {
  :kind=>4,
  :pubkey=>"a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466",
  :created_at=>1725387307,
  :tags=>[["p", "e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6"]],
  :content=>"Nd7n/wId1oiprUCC4WWwNw==?iv=7gIRExcyO1xystretLIPnQ==",
  :id=>"b91b3fb40128112c38dc54168b9f601c22bf8fcae6e70bb2a5f53e7f3ae44388",
  :sig=>"73edf5a6acbefdd3d76f28ba90faaabe348a24c798f8fa33797eec29e2404c33a455815a59472ecd023441df38d815f83d81b95b8cb2f2c88a52982c8f7301e9"
}
e = Nostr::Event.new(**payload)

c.decrypt(e)
puts e.content
=> "Hello Alice!"

Create a subscribtion to receive events

filter = Nostr::Filter.new(
  kinds: [1],
  authors: ["a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466"],
  since: Time.now - (60*60*24), # 24 hours ago
  limit: 10
)

c.on :connect do |message|
  puts "Connected!"
end

c.on :event do |message|
  puts ">> #{message.content}"
end

c.on :eose do |message|
  puts "Finished subscription #{message.subscription_id}"
  c.close
end

c.on :close do |message|
  puts "Connection closed"
end

c.subscribe(filter: filter)
c.connect

About

A ruby library to interact with the Nostr Protocol

Topics

Resources

License

Stars

Watchers

Forks

Contributors 4

  •  
  •  
  •  
  •  

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.