Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This script (sob.rb) allows you to automatically post and update OpenBazaar listings on the STEEM blockchain. See: https://steemit.com/radiator/@inertia/sob-rb-automatically-post-openbazaar-listings-on-steem-openbazaar
This script (sob.rb) allows you to automatically post and update OpenBazaar listings on the STEEM blockchain.
sob.log
.rvmrc
  • Title: sob.rb - Automatically Post OpenBazaar Listings on STEEM #openbazaar
  • Tags: radiator ruby steem steemdev openbazaar
  • Notes:

Overview

This script (sob.rb) allows you to automatically post and update OpenBazaar listings on the STEEM blockchain.


Install

To use this Radiator script:

Linux
$ sudo apt-get install ruby-full git openssl libssl1.0.0 libssl-dev
$ gem install bundler
macOS
$ gem install bundler

I've tested it on various versions of ruby. The oldest one I got it to work was:

ruby 2.2.5, with native extensions

First, clone this gist and install the dependencies:

$ git clone https://gist.github.com/95ff25885035e6c9eae6e97ef84ba591.git sob
$ cd sob
$ bundle install

Edit the file sob.yml and set your store API information. Also set the account you want to post with. You must use the posting-wif.


Upgrade

Typically, you can upgrade to the latest version by this command, from the original directory you cloned into:

$ git pull

Usually, this works fine as long as you haven't modified anything. If you get an error, try this:

$ git stash --all
$ git pull --rebase
$ git stash pop

If you're still having problems, I suggest starting a new clone.


Usage

To do the actual post/update (this will post all contracts):

$ ruby sob.rb contract:all broadcast:true

The sob.rb script will now post all contracts on your store. If you update any of your contracts, you can run the same script again to update the blockchain. When updating, in order to avoid blockchain bloat, this script will automatically send a diff patch of changes.

To only post/update a single contract, pass the GUID:

$ ruby sob.rb contract:<contract guid> broadcast:true

Check here to see an updated version of this script:

https://gist.github.com/inertia186/95ff25885035e6c9eae6e97ef84ba591


Troubleshooting

Problem: What does this error mean?
sob.yml:1: syntax error, unexpected ':', expecting end-of-input
Solution: You ran ruby sob.yml but you should run ruby sob.rb.

![](https://cl.ly/233t0q0v2j0p/obarc-logo.png)

See my previous Ruby How To posts in: #radiator #ruby

Get in touch!

If you're using sob, I'd love to hear from you. Drop me a line and tell me what you think! I'm @inertia on STEEM and SteemSpeak.

License

I don't believe in intellectual "property". If you do, consider sob as licensed under a Creative Commons CC0 License.

source 'https://rubygems.org'
gem 'radiator', git: 'https://github.com/inertia186/radiator.git'
gem 'obarc'
gem 'activeview'
gem 'diff_match_patch_native'
GIT
remote: https://github.com/inertia186/radiator.git
revision: 86c13a6fbd5e0eba0afc826abc88792d5e6249ca
specs:
radiator (0.1.0)
bitcoin-ruby (= 0.0.10)
ffi (= 1.9.17)
hashie (>= 1.1)
logging (~> 2.1.0)
net-http-persistent (= 2.9.4)
GEM
remote: https://rubygems.org/
specs:
actionpack (5.0.2)
actionview (= 5.0.2)
activesupport (= 5.0.2)
rack (~> 2.0)
rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.0.2)
activesupport (= 5.0.2)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activesupport (5.0.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
activeview (0)
activesupport (~> 5.0)
railties (~> 5.0)
bitcoin-ruby (0.0.10)
builder (3.2.3)
concurrent-ruby (1.0.5)
diff_match_patch_native (1.0.2)
rice (>= 1.4.2)
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
erubis (2.7.0)
ffi (1.9.17)
hashie (3.5.5)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (0.8.1)
little-plugger (1.1.4)
logging (2.1.0)
little-plugger (~> 1.1)
multi_json (~> 1.10)
loofah (2.0.3)
nokogiri (>= 1.5.9)
method_source (0.8.2)
mime-types (2.99.3)
mini_portile2 (2.1.0)
minitest (5.10.1)
multi_json (1.12.1)
net-http-persistent (2.9.4)
netrc (0.11.0)
nokogiri (1.7.1)
mini_portile2 (~> 2.1.0)
obarc (0.5.2)
logging (~> 2.1, >= 2.1.0)
rest-client (~> 1.8, >= 1.8.0)
rack (2.0.1)
rack-test (0.6.3)
rack (>= 1.0)
rails-dom-testing (2.0.2)
activesupport (>= 4.2.0, < 6.0)
nokogiri (~> 1.6)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
railties (5.0.2)
actionpack (= 5.0.2)
activesupport (= 5.0.2)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (12.0.0)
rest-client (1.8.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
rice (2.1.1)
thor (0.19.4)
thread_safe (0.3.6)
tzinfo (1.2.3)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
PLATFORMS
ruby
DEPENDENCIES
activeview
diff_match_patch_native
obarc
radiator!
BUNDLED WITH
1.14.3

${category}; ${currency_code}: ${price}

${images}

Description

${description}

Tags

${tags}

Ships To

${ships_to}

Return Policy

${returns}

Terms and Conditions

${terms_conditions}

Buy on OpenBazaar
:session_options:
:protocol: http
:server_host: localhost
:server_port: 18469
:api_version: v1
:verify_ssl: false
:username: username
:password: password
:account:
:name: <Your GOLOS Account>
:posting_wif: <Your Posting WIF>
:chain_options:
:chain: :golos
:url: https://ws.golos.io
require 'rubygems'
require 'bundler/setup'
require 'yaml'
require 'action_view'
require 'diff_match_patch_native'
Bundler.require
include ActionView::Helpers::TextHelper
include ActionView::Helpers::DateHelper
include ActionView::Helpers::NumberHelper
AGENT_ID = "sob/0.0.1"
@listing_template_path = "#{File.dirname(__FILE__)}/listing_template.md"
@listing_template = File.open(@listing_template_path).read
@config_path = __FILE__.sub(/\.rb$/, '.yml')
@config = YAML.load_file(@config_path)
@ob_session_options = @config[:session_options]
@account = @config[:account]
@chain_options = @config[:chain_options]
@ob_session = OBarc::Session.new(@ob_session_options)
@api = Radiator::Api.new(@chain_options)
@profile = @ob_session.profile['profile']
ARGV.each do |arg|
if arg =~ /contracts:.+/
@contracts = arg.split('contracts:').last.to_s.split(',')
end
if arg =~ /broadcast:.+/
@broadcast = arg.split('broadcast:').last.to_s.downcase == 'true' rescue false
end
end
if @contracts.nil? || @contracts.empty?
puts "Usage:"
puts "\truby #{__FILE__} contracts:<guids> broadcast:true"
puts "\ncontract can either be guids searated by ',' or all."
exit
end
def merge_listing_template(contract)
merged = @listing_template.dup
vendor_offer = contract['vendor_offer']
listing = vendor_offer['listing']
item = listing['item']
price_per_unit = item['price_per_unit']
policy = listing['policy']
merged = merged.gsub('${store_id}', @profile['guid'])
merged = merged.gsub('${contract_id}', listing['contract_id'])
merged = merged.gsub('${currency_code}', price_per_unit['fiat']['currency_code'])
merged = merged.gsub('${price}', number_with_precision(price_per_unit['fiat']['price'], precision: 2, delimiter: ',', separator: '.'))
merged = merged.gsub('${category}', item['category'])
merged = merged.gsub('${images}', images(contract))
merged = merged.gsub('${description}', description(contract))
merged = merged.gsub('${tags}', item['keywords'].join(', '))
merged = merged.gsub('${ships_to}', ships_to(contract))
merged = merged.gsub('${returns}', policy['returns'])
merged = merged.gsub('${terms_conditions}', policy['terms_conditions'])
merged
end
def description(contract)
vendor_offer = contract['vendor_offer']
listing = vendor_offer['listing']
item = listing['item']
description = item['description']
description.force_encoding('BINARY').encode('UTF-8', invalid: :replace, undef: :replace, replace: ' ')
end
def images(contract)
vendor_offer = contract['vendor_offer']
listing = vendor_offer['listing']
item = listing['item']
image_hashes = item['image_hashes']
image_hashes.map do |hash|
"<img src=\"https://duosear.ch/images/#{hash}.jpg\" />\n\n"
end.join
end
def ships_to(contract)
vendor_offer = contract['vendor_offer']
listing = vendor_offer['listing']
shipping = listing['shipping']
shipping_regions = shipping['shipping_regions']
shipping_regions.map do |region|
region.titleize
end.join(', ')
end
def tags(contract)
vendor_offer = contract['vendor_offer']
listing = vendor_offer['listing']
item = listing['item']
keywords = item['keywords']
['openbazaar'] + keywords[0..3]
end
puts "Lookup listings for: #{@profile['name']}"
store_guid = @profile['guid']
@ob_session.listings['listings'].each do |listing|
contract_hash = listing['contract_hash']
if @contracts == ['all'] || @contracts.include?(contract_hash)
contract = @ob_session.contracts(id: contract_hash)
if contract.empty?
puts "Skipping contract (not found): #{contract_hash}"
end
response = @api.get_content(@account[:name], contract_hash)
original_post = response.result
body = if !!original_post.body.nil?
updating = false
merge_listing_template(contract)
else
updating = true
dmp = DiffMatchPatch.new
patches = dmp.patch_make original_post.body, merge_listing_template(contract)
dmp.patch_to_text(patches)
end
if body.size == 0
puts "Skipping, nothing to post."
next
end
metadata = {
tags: tags(contract),
app: AGENT_ID
}
post = {
type: :comment,
parent_permlink: tags(contract).first,
author: @account[:name],
title: listing['title'],
permlink: contract_hash,
body: body,
json_metadata: metadata.to_json,
parent_author: ''
}
slug = "@#{post[:author]}/#{post[:permlink]}"
tx = Radiator::Transaction.new(@chain_options.merge(wif: @account[:posting_wif]))
op = Radiator::Operation.new(post)
tx.operations << op
response = tx.process(@broadcast)
if defined?(response.error) && !!response.error
puts response.error.message
exit
end
if defined?(response.result)
if updating
print "Updated Listing: "
else
print "Created Listing: "
end
puts "#{response.result.to_json}\n\thttps://steemit.com/#{post[:parent_permlink]}/#{slug}"
else
puts "Dry Run: to post contract, use: ruby #{__FILE__} @contracts:#{contract_hash} broadcast:true\n\thttps://steemit.com/openbazaar/@#{@account[:name]}/#{contract_hash}"
end
end
end
:session_options:
:protocol: http
:server_host: localhost
:server_port: 18469
:api_version: v1
:verify_ssl: false
:username: username
:password: password
:account:
:name: <Your STEEM Account>
:posting_wif: <Your Posting WIF>
:chain_options:
:chain: :steem
:url: https://steemd.steemit.com
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.