obarc 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +19 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +12 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +78 -0
- data/README.md +133 -0
- data/Rakefile +39 -0
- data/doc/OBarc.html +210 -0
- data/doc/OBarc/Api.html +883 -0
- data/doc/OBarc/Session.html +5399 -0
- data/doc/OBarc/Utils.html +117 -0
- data/doc/OBarc/Utils/Exceptions.html +115 -0
- data/doc/OBarc/Utils/Exceptions/InvalidArgumentError.html +230 -0
- data/doc/OBarc/Utils/Exceptions/InvalidElementError.html +218 -0
- data/doc/OBarc/Utils/Exceptions/InvalidWordError.html +218 -0
- data/doc/OBarc/Utils/Exceptions/MissingArgumentError.html +230 -0
- data/doc/OBarc/Utils/Exceptions/OBarcArgumentError.html +151 -0
- data/doc/OBarc/Utils/Exceptions/OBarcError.html +291 -0
- data/doc/OBarc/Utils/Exceptions/OBarcResponseError.html +520 -0
- data/doc/OBarc/Utils/Exceptions/OverLimitError.html +218 -0
- data/doc/OBarc/Utils/Exceptions/TimeOutError.html +218 -0
- data/doc/OBarc/Utils/Helper.html +115 -0
- data/doc/OBarc/Utils/Helper/ObjectExtensions.html +105 -0
- data/doc/_index.html +239 -0
- data/doc/class_list.html +58 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file.README.html +229 -0
- data/doc/file_list.html +60 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +229 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +181 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +465 -0
- data/doc/top-level-namespace.html +112 -0
- data/graphics/obarc-logo.png +0 -0
- data/graphics/obarc-logo.xcf +0 -0
- data/lib/obarc.rb +14 -0
- data/lib/obarc/api.rb +220 -0
- data/lib/obarc/session.rb +514 -0
- data/lib/obarc/utils/exceptions.rb +35 -0
- data/lib/obarc/utils/helper.rb +18 -0
- data/lib/obarc/version.rb +3 -0
- data/obarc.gemspec +32 -0
- metadata +276 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.8.7.6
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
hasFrames = window.top.frames.main ? true : false;
|
19
|
+
relpath = '';
|
20
|
+
framesUrl = "frames.html#!top-level-namespace.html";
|
21
|
+
</script>
|
22
|
+
|
23
|
+
|
24
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
25
|
+
|
26
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
27
|
+
|
28
|
+
|
29
|
+
</head>
|
30
|
+
<body>
|
31
|
+
<div id="header">
|
32
|
+
<div id="menu">
|
33
|
+
|
34
|
+
<a href="_index.html">Index</a> »
|
35
|
+
|
36
|
+
|
37
|
+
<span class="title">Top Level Namespace</span>
|
38
|
+
|
39
|
+
|
40
|
+
<div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<div id="search">
|
44
|
+
|
45
|
+
<a class="full_list_link" id="class_list_link"
|
46
|
+
href="class_list.html">
|
47
|
+
Class List
|
48
|
+
</a>
|
49
|
+
|
50
|
+
<a class="full_list_link" id="method_list_link"
|
51
|
+
href="method_list.html">
|
52
|
+
Method List
|
53
|
+
</a>
|
54
|
+
|
55
|
+
<a class="full_list_link" id="file_list_link"
|
56
|
+
href="file_list.html">
|
57
|
+
File List
|
58
|
+
</a>
|
59
|
+
|
60
|
+
</div>
|
61
|
+
<div class="clear"></div>
|
62
|
+
</div>
|
63
|
+
|
64
|
+
<iframe id="search_frame"></iframe>
|
65
|
+
|
66
|
+
<div id="content"><h1>Top Level Namespace
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
</h1>
|
71
|
+
|
72
|
+
<dl class="box">
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
</dl>
|
82
|
+
<div class="clear"></div>
|
83
|
+
|
84
|
+
<h2>Defined Under Namespace</h2>
|
85
|
+
<p class="children">
|
86
|
+
|
87
|
+
|
88
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="OBarc.html" title="OBarc (module)">OBarc</a></span>
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
</p>
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
</div>
|
104
|
+
|
105
|
+
<div id="footer">
|
106
|
+
Generated on Tue Apr 4 17:29:29 2017 by
|
107
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
108
|
+
0.8.7.6 (ruby-2.2.5).
|
109
|
+
</div>
|
110
|
+
|
111
|
+
</body>
|
112
|
+
</html>
|
Binary file
|
Binary file
|
data/lib/obarc.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'obarc/version'
|
2
|
+
require 'json'
|
3
|
+
require 'rest-client'
|
4
|
+
|
5
|
+
module OBarc
|
6
|
+
require 'obarc/utils/helper'
|
7
|
+
require 'obarc/session'
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def login!(options)
|
11
|
+
options[:base_url] ||= "#{options[:protocol]}://#{options[:server_host]}:#{options[:server_port]}/api/#{options[:api_version]}"
|
12
|
+
Session.new(username: options[:username], password: options[:password], cookies: Api.post_login(options).cookies)
|
13
|
+
end
|
14
|
+
end
|
data/lib/obarc/api.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'obarc/utils/helper'
|
2
|
+
require 'obarc/utils/exceptions'
|
3
|
+
require 'uri'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module OBarc
|
7
|
+
module Api
|
8
|
+
extend self
|
9
|
+
using Utils::Helper::ObjectExtensions
|
10
|
+
|
11
|
+
DEFAULT_TIMEOUT = 60 * 60 * 1
|
12
|
+
|
13
|
+
VALID_ACTIONS = {
|
14
|
+
get: %i(image profile listings followers following contracts shutdown
|
15
|
+
settings connected_peers routing_table notifications chat_messages
|
16
|
+
chat_conversations sales purchases order cases order_messages ratings
|
17
|
+
btc_price),
|
18
|
+
post: %i(login follow unfollow profile social_accounts
|
19
|
+
contract make_moderator unmake_moderator purchase_contract
|
20
|
+
confirm_order upload_image complete_order settings
|
21
|
+
mark_notification_as_read broadcast mark_chat_message_as_read
|
22
|
+
check_for_payment dispute_contract close_dispute release_funds refund
|
23
|
+
mark_discussion_as_read),
|
24
|
+
delete: %i(social_accounts contract chat_conversation)
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
# POST api/v1/login
|
28
|
+
def post_login(options = {})
|
29
|
+
url = "#{build_base_url(options)}/login"
|
30
|
+
auth = build_authentication(options)
|
31
|
+
|
32
|
+
raise Utils::Exceptions::MissingArgumentError, [:username, :password] if auth.empty?
|
33
|
+
|
34
|
+
execute method: :post, url: url, headers: {params: auth},
|
35
|
+
verify_ssl: build_verify_ssl(options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ping(options = {})
|
39
|
+
execute(method: :get,
|
40
|
+
url: "#{build_base_url(options)}/connected_peers?_=#{Time.now.to_i}",
|
41
|
+
headers: build_headers(options), verify_ssl: build_verify_ssl(options))
|
42
|
+
end
|
43
|
+
|
44
|
+
def respond_to_missing?(m, include_private = false)
|
45
|
+
verb = m.to_s.split('_')
|
46
|
+
rest_method = verb[0].to_sym
|
47
|
+
return false unless VALID_ACTIONS.keys.include?(rest_method)
|
48
|
+
|
49
|
+
endpoint = verb[1..-1].join('_')
|
50
|
+
return false if endpoint.nil?
|
51
|
+
|
52
|
+
VALID_ACTIONS[rest_method].include?(endpoint.to_sym)
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(m, *args, &block)
|
56
|
+
super unless respond_to_missing?(m)
|
57
|
+
|
58
|
+
# Many of the calls to restapi.py are uniform enough for DRY code, but the
|
59
|
+
# ones that aren't are mapped here.
|
60
|
+
|
61
|
+
rest_method, endpoint, params, options = case m
|
62
|
+
when :get_image then [:get, 'get_image', args[0], args[1]]
|
63
|
+
when :get_listings then [:get, 'get_listings', args[0], args[1]]
|
64
|
+
when :get_followers then [:get, 'get_followers', args[0], args[1]]
|
65
|
+
when :get_following then [:get, 'get_following', args[0], args[1]]
|
66
|
+
when :post_contract then [:post, 'contracts', args[0], args[1]]
|
67
|
+
when :delete_contract then [:delete, 'contracts', args[0], args[1]]
|
68
|
+
when :get_notifications then [:get, 'get_notifications', nil, args[0]]
|
69
|
+
when :get_chat_conversations then [:get, 'get_chat_conversations', nil, args[0]]
|
70
|
+
when :get_sales then [:get, 'get_sales', nil, args[0]]
|
71
|
+
when :get_purchases then [:get, 'get_purchases', nil, args[0]]
|
72
|
+
when :get_cases then [:get, 'get_cases', nil, args[0]]
|
73
|
+
when :get_ratings then [:get, 'get_ratings', args[0], args[1]]
|
74
|
+
else
|
75
|
+
verb = m.to_s.split('_')
|
76
|
+
a = [verb[0].to_sym, verb[1..-1].join('_')]
|
77
|
+
a << if args.size == 1
|
78
|
+
nil
|
79
|
+
else
|
80
|
+
args[0]
|
81
|
+
end
|
82
|
+
a << args[args.size - 1]
|
83
|
+
end
|
84
|
+
|
85
|
+
url = "#{build_base_url(options)}/#{endpoint}" if !!endpoint && !!options
|
86
|
+
if !!rest_method && !!url && !!options
|
87
|
+
headers = build_headers(options)
|
88
|
+
headers = headers.merge(params: params.compact) if !!params
|
89
|
+
return execute method: rest_method, url: url, headers: headers,
|
90
|
+
verify_ssl: build_verify_ssl(options)
|
91
|
+
end
|
92
|
+
|
93
|
+
raise Utils::Exceptions::OBarcError, "Did not handle #{m} as expected, arguments: #{args}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# POST api/v1/upload_image
|
97
|
+
def post_upload_image(options = {})
|
98
|
+
elements = [:image, :avatar, :header]
|
99
|
+
params = options.slice(*elements)
|
100
|
+
options = options.delete_if { |k, v| elements.include? k }
|
101
|
+
|
102
|
+
url = "#{build_base_url(options)}/upload_images"
|
103
|
+
execute method: :post, url: url,
|
104
|
+
headers: build_headers(options).merge(params: params.compact),
|
105
|
+
verify_ssl: build_verify_ssl(options)
|
106
|
+
end
|
107
|
+
|
108
|
+
# GET api/v1/contracts
|
109
|
+
def get_contracts(contracts, options = {})
|
110
|
+
id = contracts[:id]
|
111
|
+
guid = contracts[:guid]
|
112
|
+
|
113
|
+
if !!id && id.size != 40
|
114
|
+
raise Utils::Exceptions::InvalidArgumentError, id: "must be 40 characters, if present (was: #{id.size})"
|
115
|
+
elsif !!guid && guid.size != 40
|
116
|
+
raise Utils::Exceptions::InvalidArgumentError, guid: "must be 40 characters, if present (was: #{guid.size})"
|
117
|
+
end
|
118
|
+
|
119
|
+
url = "#{build_base_url(options)}/contracts"
|
120
|
+
execute method: :get, url: url,
|
121
|
+
headers: build_headers(options).merge(params: contracts.compact),
|
122
|
+
verify_ssl: build_verify_ssl(options)
|
123
|
+
end
|
124
|
+
|
125
|
+
# GET api/v1/get_chat_messages
|
126
|
+
def get_chat_messages(chat_messages, options = {})
|
127
|
+
guid = chat_messages[:guid]
|
128
|
+
|
129
|
+
if guid.nil? || guid.size != 40
|
130
|
+
raise Utils::Exceptions::InvalidArgumentError, guid: "must be present, 40 characters (was: #{guid.inspect})"
|
131
|
+
end
|
132
|
+
|
133
|
+
url = "#{build_base_url(options)}/get_chat_messages"
|
134
|
+
execute method: :get, url: url,
|
135
|
+
headers: build_headers(options).merge(params: chat_messages.compact),
|
136
|
+
verify_ssl: build_verify_ssl(options)
|
137
|
+
end
|
138
|
+
|
139
|
+
# GET api/v1/get_order
|
140
|
+
def get_order(order, options = {})
|
141
|
+
order_id = order[:order_id]
|
142
|
+
|
143
|
+
if order_id.nil?
|
144
|
+
raise Utils::Exceptions::InvalidArgumentError, order_id: 'must be present'
|
145
|
+
end
|
146
|
+
|
147
|
+
url = "#{build_base_url(options)}/get_order"
|
148
|
+
execute method: :get, url: url,
|
149
|
+
headers: build_headers(options).merge(params: order.compact),
|
150
|
+
verify_ssl: build_verify_ssl(options)
|
151
|
+
end
|
152
|
+
private
|
153
|
+
def execute(options = {})
|
154
|
+
if options[:method] == :post
|
155
|
+
options[:headers][:content_type] = 'application/x-www-form-urlencoded'
|
156
|
+
end
|
157
|
+
|
158
|
+
if !!options[:headers][:params]
|
159
|
+
params = options[:headers].delete(:params)
|
160
|
+
|
161
|
+
if params.values.map(&:class).include? Array
|
162
|
+
# Dropping to a lower level for parameters since they're more
|
163
|
+
# complicated due to the presense of an Array. Note that these
|
164
|
+
# parameters go # outside the header.
|
165
|
+
options[:url] += "?#{URI::encode_www_form params}"
|
166
|
+
elsif params.values.map(&:class).any? { |c| [Tempfile, File, StringIO].include?(c) }
|
167
|
+
# Handling (possibly) large files.
|
168
|
+
options[:payload] = {multipart: true}
|
169
|
+
params.each do |k, v|
|
170
|
+
if v.respond_to? :read
|
171
|
+
options[:payload][k] = Base64.strict_encode64(v.read)
|
172
|
+
else
|
173
|
+
options[:payload][k] = v
|
174
|
+
# FIXME Might want to warn that we are possibly mixing multipart
|
175
|
+
# with simple payload.
|
176
|
+
end
|
177
|
+
end
|
178
|
+
else
|
179
|
+
options[:headers][:params] = params
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
RestClient::Request.execute(options.merge(timeout: DEFAULT_TIMEOUT))
|
184
|
+
end
|
185
|
+
|
186
|
+
def build_authentication(options = {})
|
187
|
+
if options.kind_of? Session
|
188
|
+
{username: options.username, password: options.password}
|
189
|
+
else
|
190
|
+
options.slice(:username, :password)
|
191
|
+
end.compact
|
192
|
+
end
|
193
|
+
|
194
|
+
def build_base_url(options = {})
|
195
|
+
if options.kind_of? Session
|
196
|
+
options.base_url
|
197
|
+
elsif options.kind_of? Hash
|
198
|
+
options[:base_url]
|
199
|
+
else
|
200
|
+
raise Utils::Exceptions::OBarcError, "Unable to build base URL using: #{options.inspect}, expected a OBarc::Session or Hash."
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def build_headers(options = {})
|
205
|
+
if options.kind_of? Session
|
206
|
+
{cookies: options.cookies}
|
207
|
+
else
|
208
|
+
options.slice(:cookies)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def build_verify_ssl(options = {})
|
213
|
+
if options.kind_of? Session
|
214
|
+
!!options.verify_ssl
|
215
|
+
else
|
216
|
+
!!options[:verify_ssl]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,514 @@
|
|
1
|
+
require 'obarc/api'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'logging'
|
4
|
+
|
5
|
+
module OBarc
|
6
|
+
class Session
|
7
|
+
OPTIONS_KEYS = %i(protocol server_host server_port api_version username
|
8
|
+
password base_url logger cookies verify_ssl)
|
9
|
+
|
10
|
+
attr_accessor :cookies, :username, :password, :verify_ssl
|
11
|
+
attr_writer :base_url, :logger
|
12
|
+
|
13
|
+
DEFAULT_OPTIONS = {
|
14
|
+
protocol: 'http',
|
15
|
+
server_host: 'localhost',
|
16
|
+
server_port: '18469',
|
17
|
+
api_version: 'v1',
|
18
|
+
verify_ssl: true
|
19
|
+
}
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
OPTIONS_KEYS.each do |k|
|
23
|
+
value = if options.key?(k)
|
24
|
+
options[k]
|
25
|
+
else
|
26
|
+
DEFAULT_OPTIONS[k]
|
27
|
+
end
|
28
|
+
|
29
|
+
instance_variable_set "@#{k}".to_sym, value
|
30
|
+
end
|
31
|
+
|
32
|
+
@base_url ||= base_url
|
33
|
+
@logger ||= logger
|
34
|
+
@cookies ||= Api::post_login(self).cookies
|
35
|
+
end
|
36
|
+
|
37
|
+
def base_url
|
38
|
+
@base_url ||= "#{@protocol}://#{@server_host}:#{@server_port}/api/#{@api_version}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger
|
42
|
+
@logger ||= Logging.logger(STDOUT)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Check if there's a valid session.
|
46
|
+
#
|
47
|
+
# @return [Boolean] True if the session is valid.
|
48
|
+
def ping
|
49
|
+
return false if !(json = Api::ping(self))
|
50
|
+
!!JSON[json]['num_peers']
|
51
|
+
rescue RestClient::Unauthorized => e
|
52
|
+
logger.warn(e)
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the image for the hash specified.
|
57
|
+
# @param image [Hash] containing the hash: of the target image, required
|
58
|
+
# @return [Object] The image will be returned in .jpg format.
|
59
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-image
|
60
|
+
def image(image); Api::get_image(image, self); end
|
61
|
+
|
62
|
+
# Returns the profile data of the user’s node, or that of a target node.
|
63
|
+
#
|
64
|
+
# @param profile [Hash] containing the guid: of the target node, optional
|
65
|
+
# * The global unique identifier (guid; 40 character hex string) of the node to get the profile data from
|
66
|
+
# * If the guid is omitted, your own node’s profile will be returned
|
67
|
+
# @return [Hash]
|
68
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-profile
|
69
|
+
def profile(profile = nil); JSON[Api::get_profile(profile, self)]; end
|
70
|
+
|
71
|
+
# Returns just the profile's social accounts of the user’s node, or that of a target node.
|
72
|
+
#
|
73
|
+
# @param profile [Hash] containing the guid: of the target node, optional
|
74
|
+
# * The global unique identifier (guid; 40 character hex string) of the node to get the profile data from
|
75
|
+
# * If the guid is omitted, your own node’s social accounts will be returned
|
76
|
+
# @return [Hash]
|
77
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-profile
|
78
|
+
def social_accounts(profile = nil)
|
79
|
+
result = JSON[Api::get_profile(profile, self)]
|
80
|
+
|
81
|
+
if !!result && !!result['profile'] && !!result['profile']['social_accounts']
|
82
|
+
result['profile']['social_accounts']
|
83
|
+
else
|
84
|
+
[]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the listings of the user’s node, or that of a target node.
|
89
|
+
#
|
90
|
+
# @param listings [Hash] containing the guid: of the target node, optional
|
91
|
+
# * If the guid is omitted, the server will look for listings in your own node’s database.
|
92
|
+
# @return [Hash]
|
93
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_listings
|
94
|
+
def listings(listings = nil); JSON[Api::get_listings(listings, self)]; end
|
95
|
+
|
96
|
+
# Finds the listings of the user’s node, or that of a target node.
|
97
|
+
#
|
98
|
+
# @param options [Hash] containing:
|
99
|
+
# * guid: of the target node, optional
|
100
|
+
# *If the guid is omitted, the server will look for listings in your own node’s database.
|
101
|
+
# * pattern: [Regex] search phrase
|
102
|
+
# @return [Hash]
|
103
|
+
def query_listings(options = {})
|
104
|
+
pattern = options.delete(:pattern)
|
105
|
+
all_listings = JSON[Api::get_listings(options, self)]
|
106
|
+
listings = all_listings['listings']
|
107
|
+
|
108
|
+
if !!pattern
|
109
|
+
listings = listings.select do |l|
|
110
|
+
[l['contract_hash'], l['category'], l['title'],
|
111
|
+
l['price'].to_s, l['origin'], l['currency_code'],
|
112
|
+
l['ships_to'].join].join(' ') =~ pattern
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
return {'listings' => listings} if listings === all_listings
|
117
|
+
|
118
|
+
start = Time.now.to_i
|
119
|
+
|
120
|
+
@cache_timestamp = if (start - (@cache_timestamp ||= 0)) > 300
|
121
|
+
@contracts_cache = {}
|
122
|
+
start
|
123
|
+
else
|
124
|
+
@cache_timestamp
|
125
|
+
end
|
126
|
+
|
127
|
+
@contracts_cache ||= {}
|
128
|
+
(all_listings['listings'] - listings).each do |listing|
|
129
|
+
contract_hash = listing['contract_hash']
|
130
|
+
contract = @contracts_cache[contract_hash] ||= contracts(options.merge(id: listing['contract_hash']))
|
131
|
+
next unless !!contract
|
132
|
+
|
133
|
+
l = contract['vendor_offer']['listing']
|
134
|
+
|
135
|
+
if [l['metadata']['expiry'], l['item']['category'], l['item']['sku'],
|
136
|
+
l['item']['description'], l['item']['process_time'],
|
137
|
+
l['item']['keywords'].join].join(' ') =~ pattern
|
138
|
+
listings << listing && next
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
{'listings' => listings}
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the followers of the user’s node, or that of a target node.
|
146
|
+
#
|
147
|
+
# @param followers [Hash] containing the guid: of the target node, optional
|
148
|
+
# * If the guid is omitted, the server will look for followers in your own node’s database.
|
149
|
+
# @return [Hash]
|
150
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_followers
|
151
|
+
def followers(followers = nil); JSON[Api::get_followers(followers, self)]; end
|
152
|
+
|
153
|
+
# Returns the following of the user’s node, or that of a target node.
|
154
|
+
#
|
155
|
+
# @param following [Hash] containing the guid: of the target node, optional
|
156
|
+
# * If the guid is omitted, the server will look for following in your own node’s database.
|
157
|
+
# @return [Hash]
|
158
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_following
|
159
|
+
def following(following = nil); JSON[Api::get_following(following, self)]; end
|
160
|
+
|
161
|
+
# Follows a target node and will cause you to receive notifications from
|
162
|
+
# that node after certain event (e.g. new listing, broadcast messages) and
|
163
|
+
# share some metadata (in future).
|
164
|
+
#
|
165
|
+
# @param follow [Hash] containing the guid: of the target node, required
|
166
|
+
# @return [Hash] containing: "success" => true or false
|
167
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-follow
|
168
|
+
def follow(follow); JSON[Api::post_follow(follow, self)]; end
|
169
|
+
|
170
|
+
# Stop following a target node, will cease to receive notifications and
|
171
|
+
# sharing metadata.
|
172
|
+
#
|
173
|
+
# @param unfollow [Hash] containing the guid: of the target node, required
|
174
|
+
# @return [Hash] containing: "success" => true or false
|
175
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-unfollow
|
176
|
+
def unfollow(unfollow); JSON[Api::post_unfollow(unfollow, self)]; end
|
177
|
+
|
178
|
+
# Add data related to the node's profile into the database, which will be
|
179
|
+
# visible to other nodes.
|
180
|
+
#
|
181
|
+
# @param profile [Hash]
|
182
|
+
# @return [Hash] containing: "success" => true or false
|
183
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-profile
|
184
|
+
def update_profile(profile = {}); JSON[Api::post_profile(profile, self)]; end
|
185
|
+
|
186
|
+
# Adds a social account to the user profile data of the user.
|
187
|
+
#
|
188
|
+
# @param social_account [Hash] e.g.: account_type: 'TWITTER', username: '@inertia186'
|
189
|
+
# @return [Hash] containing: "success" => true or false
|
190
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-social_accounts
|
191
|
+
def add_social_account(social_account = {}); JSON[Api::post_social_accounts(social_account, self)]; end
|
192
|
+
|
193
|
+
# Undocumented.
|
194
|
+
#
|
195
|
+
# @return [Hash]
|
196
|
+
def delete_social_account(social_account = {}); JSON[Api::delete_social_accounts(social_account, self)]; end
|
197
|
+
|
198
|
+
# Retrieves the listings created by either your node or a target node.
|
199
|
+
#
|
200
|
+
# @param contracts [Hash] containing the:
|
201
|
+
# * id: Unique identifier of the contract SHA256 of the JSON formatted contract (40 character hex string), required
|
202
|
+
# * guid: GUID of the node if the call is made to a target node. If omitted, the API will search for a contract ID created by your own node
|
203
|
+
# @return [Hash]
|
204
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-contracts
|
205
|
+
def contracts(contracts = {}); JSON[Api::get_contracts(contracts, self)]; end
|
206
|
+
|
207
|
+
# Creates a listing contract, which is saved to the database and local file
|
208
|
+
# system, as well as publish the keywords in the distributed hash table.
|
209
|
+
#
|
210
|
+
# @param contract [Hash] containing:
|
211
|
+
# * expiration_date: [UTC] Formatted string. The date the contract should expire in string formatted UTC datetime. Example:
|
212
|
+
# * "2015-11-01T00:00 UTC"
|
213
|
+
# * "" if the contract never expires
|
214
|
+
# * metadata_category: [category] Formatted string. Select from:
|
215
|
+
# * physical good
|
216
|
+
# * digital good
|
217
|
+
# * service
|
218
|
+
# * title: [title text] String. Title of the product for sale
|
219
|
+
# * description: [description text] String. Description of the item, content or service
|
220
|
+
# * currency_code: [code] Formatted string. The currency the product is priced in. may either be “btc” or a currency from this list
|
221
|
+
# * price: [value] String. The price per unit in the same currency as currency_code.
|
222
|
+
# * process_time: [time] String. The time it will take to prepare the item for shipping
|
223
|
+
# * nsfw: [true/false] Boolean. Is the item not suitable for work (i.e. 18+)
|
224
|
+
# * shipping_origin: [country/region] Formatted string. Required and only applicable if the metadata_category is a physical good
|
225
|
+
# * Where the item ships from
|
226
|
+
# * Must be a formatted string from this list
|
227
|
+
# * shipping_regions: [locations] Formatted string. Required and only applicable if the metadata_category is a physical good.
|
228
|
+
# * A list of countries/regions where the product will ship to
|
229
|
+
# * Each item in the list must be formatted from this list
|
230
|
+
# * est_delivery_domestic: [time] Estimated delivery time for domestic shipments
|
231
|
+
# * est_delivery_international: [time] String. Estimated delivery time for international shipments.
|
232
|
+
# * terms_conditions: [terms and conditions text] String. Any terms or conditions the user wishes to include.
|
233
|
+
# * returns: [returns policy text] String. Return policy.
|
234
|
+
# * shipping_currency_code: [currency code] Formatted string. The currency code used to price shipping. may either be “btc” or a currency from this list.
|
235
|
+
# * shipping_domestic: [price] String. The price of domestic shipping in the selected currency code.
|
236
|
+
# * shipping_international: [price] String. The price of nternational shipping in the selected currency code.
|
237
|
+
# * keywords: [keyword text] String. A list of string search terms for the listing. Must be fewer than 10.
|
238
|
+
# * category: [category text] Sting. A user-generated category for this product. Will show in store’s category list.
|
239
|
+
# * condition: [condition text] The condition of the product
|
240
|
+
# * sku: [sku text] String. Stock keeping unit (sku) for the listing.
|
241
|
+
# * images: [String, Array<String>] 40 character hex string. A list of SHA256 image hashes. The images should be uploaded using the upload_image api call.
|
242
|
+
# * images: '04192728d0fd8dfe6663f429a5c03a7faf907930'
|
243
|
+
# * images: ['04192728d0fd8dfe6663f429a5c03a7faf907930', '0dee4786fd02d6bc673b50309a3c831acf78ec70']
|
244
|
+
# * image_urls: [Array<String>] An array of image URLs for OBarc to first download then automatically store to this record.
|
245
|
+
# * image_urls: 'http://i.imgur.com/YHBh57j.gif'
|
246
|
+
# * image_urls: ['http://i.imgur.com/uC2KUQ6.png', 'http://i.imgur.com/RliU8Gn.jpg']
|
247
|
+
# * image_data: [Array<String>] An array of Base64 images for OBarc to automatically store to this record.
|
248
|
+
# * image_data: <String>
|
249
|
+
# * image_data: [<String>, <String>]
|
250
|
+
# * free_shipping: [boolean] "true" or "false"
|
251
|
+
# * moderators: [guids] GUID: 40 character hex string. A list of moderator GUIDs that the vendor wishes to use
|
252
|
+
# * Note: the moderator must have been previously returned by the get_moderators websocket call.
|
253
|
+
# * Given the UI workflow, this call should always be made before the contract is set.
|
254
|
+
# * options: [options text] String. A list of options for the product. Example: “size”, “color”
|
255
|
+
# * option: [option text] String.
|
256
|
+
# * For each option in the options list, another argument should be added using that option name and a list of value
|
257
|
+
# * For example, given “color” in the options list, choose from "red", "green", "purple" etc
|
258
|
+
# @return [Hash] containing: "success" => true or false, "id" => Integer
|
259
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-contracts
|
260
|
+
def create_contract(contract = {})
|
261
|
+
# Note, passing contract_id appears to create a clone that re-uses the
|
262
|
+
# original contract_id.
|
263
|
+
|
264
|
+
%i(image_urls image_data).each do |symbol|
|
265
|
+
upload_contract_images_with(symbol, contract) if !!contract[symbol]
|
266
|
+
end
|
267
|
+
|
268
|
+
JSON[Api::post_contract(contract, self)]
|
269
|
+
end
|
270
|
+
|
271
|
+
def upload_contract_images_with(symbol, contract = {})
|
272
|
+
contract[:images] = [contract.delete(symbol)].flatten.map do |image|
|
273
|
+
response = if image.size < 2000 && image =~ URI::ABS_URI
|
274
|
+
upload_image(image: open(image, 'rb'))
|
275
|
+
else
|
276
|
+
upload_image(image: image)
|
277
|
+
end
|
278
|
+
|
279
|
+
response['image_hashes'] if response['success']
|
280
|
+
end.flatten
|
281
|
+
end
|
282
|
+
|
283
|
+
# Undocumented.
|
284
|
+
#
|
285
|
+
# @return [Hash]
|
286
|
+
def delete_contract(contract = {}); JSON[Api::delete_contract(contract, self)]; end
|
287
|
+
|
288
|
+
# Sets your node as a Moderator, which is discoverable on the network.
|
289
|
+
#
|
290
|
+
# @return [Hash] containing: "success" => true or false
|
291
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-make_moderator
|
292
|
+
def make_moderator; JSON[Api::post_make_moderator(self)]; end
|
293
|
+
|
294
|
+
# Removes the node as a Moderator and is no longer discoverable on the
|
295
|
+
# network as a Moderator.
|
296
|
+
#
|
297
|
+
# @return [Hash] containing: "success" => true or false
|
298
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post--unmake_moderator
|
299
|
+
def unmake_moderator; JSON[Api::post_unmake_moderator(self)]; end
|
300
|
+
|
301
|
+
# Purchases a contract by sending the purchase into the Vendor. The Buyer
|
302
|
+
# waits for a response to indicate whether the purchase is successful or
|
303
|
+
# not. If successful, the Buyer needs to fund the direct or multisig
|
304
|
+
# address.
|
305
|
+
#
|
306
|
+
# @param purchase_contract [Hash]
|
307
|
+
# @return [Hash] containing:
|
308
|
+
# * "success" => true or false
|
309
|
+
# * "address" => "bitcoin address to fund"
|
310
|
+
# * "amount" => "amount to fund"
|
311
|
+
# * "order_id" => "purchase order id"
|
312
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-purchase_contract
|
313
|
+
def purchase_contract(purchase_contract = {}); JSON[Api::post_purchase_contract(purchase_contract, self)]; end
|
314
|
+
|
315
|
+
# Sends the order confirmation and shipping information to the Buyer. If
|
316
|
+
# he’s offline, it will embed this data into the dht. The API call also
|
317
|
+
# updates the status of the order in the database.
|
318
|
+
#
|
319
|
+
# @param confirm_order [Hash]
|
320
|
+
# @return [Hash] containing: "success" => true or false
|
321
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-confirm_order
|
322
|
+
def confirm_order(confirm_order = {}); JSON[Api::post_confirm_order(confirm_order, self)]; end
|
323
|
+
|
324
|
+
# Saves the image in the file system and a pointer to it in the db.
|
325
|
+
#
|
326
|
+
# @param image [Hash] containing:
|
327
|
+
# * image: a list of product images to upload (LIST of images in base64. data only, no base64 prefix)
|
328
|
+
# * avatar: use this if uploading an avatar image (base64 image)
|
329
|
+
# * header: use this if uploading a header image (base64 image)
|
330
|
+
# @return [Hash] containing:
|
331
|
+
# * "success" => true or false
|
332
|
+
# * "image_hashes" => [list_of_image_hashes]
|
333
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-upload_image
|
334
|
+
def upload_image(image = {}); JSON[Api::post_upload_image(image.merge(cookies: cookies, base_url: base_url, verify_ssl: verify_ssl))]; end
|
335
|
+
|
336
|
+
# Sends a message with a partially signed transaction releasing funds from
|
337
|
+
# escrow to the Vendor as well as review data.
|
338
|
+
#
|
339
|
+
# @param complete_order [Hash]
|
340
|
+
# @return [Hash] containing: "success" => true or false
|
341
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-complete_order
|
342
|
+
def complete_order(complete_order = {}); JSON[Api::post_complete_order(complete_order, self)]; end
|
343
|
+
|
344
|
+
# Changes the settings of the node and pushes them to the database.
|
345
|
+
#
|
346
|
+
# @param settings [Hash]
|
347
|
+
# @return [Hash] containing: "success" => true or false
|
348
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-settings
|
349
|
+
def update_settings(settings = {}); JSON[Api::post_settings(settings, self)]; end
|
350
|
+
|
351
|
+
# Returns the settings of your node.
|
352
|
+
#
|
353
|
+
# @return [Hash]
|
354
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-settings
|
355
|
+
def settings; JSON[Api::get_settings(self)]; end
|
356
|
+
|
357
|
+
# Undocumented
|
358
|
+
#
|
359
|
+
# @return [Hash]
|
360
|
+
def connected_peers; JSON[Api::get_connected_peers(self)]; end
|
361
|
+
|
362
|
+
# Retreive a history of all notifications your node has received. Notifications can be sent due to:
|
363
|
+
# * A node following you
|
364
|
+
# * Events related to a purchase or sale
|
365
|
+
#
|
366
|
+
# @param FIXME not yet supported, future: limit-[number of most recent notifications]
|
367
|
+
# * Default is unlimited
|
368
|
+
# * Counts from most recent
|
369
|
+
# @return [Hash]
|
370
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-notifications
|
371
|
+
def notifications; JSON[Api::get_notifications(self)]; end
|
372
|
+
|
373
|
+
# Retrieves all chat message received from other nodes.
|
374
|
+
#
|
375
|
+
# @param chat_messages [Hash] containing:
|
376
|
+
# * guid [String] target node, required
|
377
|
+
# * limit [Integer] max number of chat messages to return (ignored)
|
378
|
+
# * start [FIXME] the starting point in the message list
|
379
|
+
# @return [Hash]
|
380
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_chat_messages
|
381
|
+
def chat_messages(chat_messages = {}); JSON[Api::get_chat_messages(chat_messages, self)]; end
|
382
|
+
|
383
|
+
# Retreives a list of outstandng conversations.
|
384
|
+
#
|
385
|
+
# @return [Hash]
|
386
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_chat_conversations
|
387
|
+
def chat_conversations; JSON[Api::get_chat_conversations(self)]; end
|
388
|
+
|
389
|
+
# Undocumented
|
390
|
+
#
|
391
|
+
# @return [Hash]
|
392
|
+
def delete_chat_conversation(delete_chat_conversation); JSON[Api::delete_chat_conversation(delete_chat_conversation, self)]; end
|
393
|
+
|
394
|
+
# Retrieves any sales made by the node.
|
395
|
+
#
|
396
|
+
# @return [Hash]
|
397
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_sales
|
398
|
+
def sales; JSON[Api::get_sales(self)]; end
|
399
|
+
|
400
|
+
# Retrieves any purchases made by the node.
|
401
|
+
#
|
402
|
+
# @return [Hash]
|
403
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-get_purchases
|
404
|
+
def purchases; JSON[Api::get_purchases(self)]; end
|
405
|
+
|
406
|
+
# Undocumented
|
407
|
+
#
|
408
|
+
# @return [Hash]
|
409
|
+
def order(order); JSON[Api::get_order(order, self)]; end
|
410
|
+
|
411
|
+
# Undocumented
|
412
|
+
#
|
413
|
+
# @return [Hash]
|
414
|
+
def cases; JSON[Api::get_cases(self)]; end
|
415
|
+
|
416
|
+
# Undocumented
|
417
|
+
#
|
418
|
+
# @return [Hash]
|
419
|
+
def order_messages(order_messages); JSON[Api::get_order_messages(order_messages, self)]; end
|
420
|
+
|
421
|
+
# Undocumented
|
422
|
+
#
|
423
|
+
# @return [Hash]
|
424
|
+
def ratings(ratings); JSON[Api::get_ratings(ratings, self)]; end
|
425
|
+
|
426
|
+
# Marks a notification as read in the database.
|
427
|
+
#
|
428
|
+
# @param notification [Hash] containing id:
|
429
|
+
# * 40 character hex string
|
430
|
+
# * Every notification has an ID that must be referenced in order to mark as read
|
431
|
+
# @return [Hash] containing: "success" => true or false
|
432
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-mark_notification_as_read
|
433
|
+
def mark_notification_as_read(notification = nil); JSON[Api::post_mark_notification_as_read(notification, self)]; end
|
434
|
+
|
435
|
+
# Sends some kind of "Twitter-like" message to all nodes that are following
|
436
|
+
# you. This call can take a while to complete.
|
437
|
+
#
|
438
|
+
# @return [Hash] containing:
|
439
|
+
# * "success" => true or false
|
440
|
+
# * "peers_reached" => [number reached]
|
441
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-broadcast
|
442
|
+
def broadcast(message = {}); JSON[Api::post_broadcast(message, self)]; end
|
443
|
+
|
444
|
+
# Undocumented
|
445
|
+
#
|
446
|
+
# @return [Hash]
|
447
|
+
def btc_price; JSON[Api::get_btc_price(self)]; end
|
448
|
+
|
449
|
+
# Undocumented
|
450
|
+
#
|
451
|
+
# @return [Hash]
|
452
|
+
def routing_table; JSON[Api::get_routing_table(self)]; end
|
453
|
+
|
454
|
+
# Marks all chat messages with a specific node as read in the database.
|
455
|
+
#
|
456
|
+
# @param chat_message_as_read [Hash] containing:
|
457
|
+
# * guid [String] GUID of the party you are chatting with
|
458
|
+
# @return [Hash] containing:
|
459
|
+
# * "success" => true or false
|
460
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-mark_chat_message_as_read
|
461
|
+
def mark_chat_message_as_read(mark_chat_message_as_read = nil); JSON[Api::post_mark_chat_message_as_read(mark_chat_message_as_read, self)]; end
|
462
|
+
|
463
|
+
# Sends a Twitter-like message to all nodes that are following you.
|
464
|
+
#
|
465
|
+
# @param check_for_payment [Hash] containing:
|
466
|
+
# * order_id [Integer]
|
467
|
+
# @return [Hash] containing:
|
468
|
+
# * "success" => true or false
|
469
|
+
# @see https://gist.github.com/drwasho/bd4b28a5a07c5a952e2f#post-check_for_payment
|
470
|
+
def check_for_payment(check_for_payment); JSON[Api::post_check_for_payment(check_for_payment, self)]; end
|
471
|
+
|
472
|
+
# Undocumented
|
473
|
+
#
|
474
|
+
# @param dispute_contract [Hash] containing:
|
475
|
+
# * order_id [Integer]
|
476
|
+
# @return [Hash]
|
477
|
+
def dispute_contract(dispute_contract = nil); JSON[Api::post_dispute_contract(dispute_contract, self)]; end
|
478
|
+
|
479
|
+
# Undocumented
|
480
|
+
#
|
481
|
+
# @param dispute_contract [Hash] containing:
|
482
|
+
# * order_id [Integer]
|
483
|
+
# * resolution [String]
|
484
|
+
# * buyer_percentage [Float]
|
485
|
+
# * vendor_percentage [Float]
|
486
|
+
# * moderator_percentage [Float]
|
487
|
+
# * moderator_address [String]
|
488
|
+
# @return [Hash]
|
489
|
+
def close_dispute(close_dispute = nil); JSON[Api::post_close_dispute(close_dispute, self)]; end
|
490
|
+
|
491
|
+
# Undocumented
|
492
|
+
#
|
493
|
+
# @return [Hash]
|
494
|
+
def release_funds(release_funds = nil); JSON[Api::post_release_funds(release_funds, self)]; end
|
495
|
+
|
496
|
+
# Undocumented
|
497
|
+
#
|
498
|
+
# @return [Hash]
|
499
|
+
def refund(refund = nil); JSON[Api::post_refund(refund, self)]; end
|
500
|
+
|
501
|
+
# Undocumented
|
502
|
+
#
|
503
|
+
# @return [Hash]
|
504
|
+
def mark_discussion_as_read(mark_discussion_as_read = nil); JSON[Api::post_mark_discussion_as_read(mark_discussion_as_read, self)]; end
|
505
|
+
|
506
|
+
# API call to cleanly disconnect from connected nodes and shutsdown the OpenBazaar server component.
|
507
|
+
#
|
508
|
+
# @return nil
|
509
|
+
# @see https://gist.github.com/drwasho/742505589f62f6aa98b4#get-shutdown
|
510
|
+
def shutdown!
|
511
|
+
Api::get_shutdown(self)
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|